DOCS / API
API reference
Base URL: https://app.glassmkr.com/api/v1. All requests and responses use JSON over HTTPS.
Authenticated endpoints require a Bearer token:
Authorization: Bearer YOUR_API_TOKEN Error responses follow a consistent envelope:
{
"error": "short_code",
"message": "Human-readable description of what went wrong."
} For programmatic-API specifics (account keys, scopes, audit log) see the Programmatic API page. For which endpoints are Free vs Pro and how 402 responses behave, see the Tier gating page.
#Authentication
Register
/auth/register PublicCreate a new Glassmkr account.
Request body:
{
"email": "[email protected]",
"password": "min-12-characters",
"name": "Jane Doe"
} Response (201):
{
"user": {
"id": "usr_a1b2c3d4",
"email": "[email protected]",
"name": "Jane Doe",
"verified": false,
"created_at": "2026-04-05T10:00:00Z"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
} A verification email is sent automatically. The account is fully functional before verification, but some features (team invites) require a verified email.
Login
/auth/login PublicAuthenticate and receive a session token.
{
"email": "[email protected]",
"password": "min-12-characters"
} Response (200):
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": "2026-04-12T10:00:00Z"
} Session tokens are valid for 7 days. Error (401) on bad credentials: { "error": "invalid_credentials", "message": "Email or password is incorrect." }.
Logout
/auth/logout AuthenticatedInvalidate the current session token. Response (204) no content.
Get current user
/auth/me AuthenticatedReturns the authenticated user's profile.
{
"id": "usr_a1b2c3d4",
"email": "[email protected]",
"name": "Jane Doe",
"verified": true,
"role": "owner",
"created_at": "2026-04-05T10:00:00Z",
"servers_count": 6,
"plan": "pro"
}Verify email
/auth/verify PublicConfirm an email address using the token from the verification email.
{ "token": "verify_abc123def456" }#Servers
Register server
/servers AuthenticatedRegister a new server. Returns a fresh collector key.
Request body: only name, hostname, and tags are accepted. Hardware fields (OS, architecture, core count, RAM) are reported by the agent on each ingest, never on registration.
{
"name": "web-prod-01",
"hostname": "web-prod-01.example.com",
"tags": ["production", "web"]
} name is required (1-100 chars). hostname defaults to name and must be a valid RFC 1035 hostname. tags is optional, max 20 strings of 1-50 chars each. Other fields are silently dropped (mass-assignment defense).
Response (201):
{
"success": true,
"server": {
"id": "srv_a1b2c3d4",
"name": "web-prod-01",
"hostname": "web-prod-01.example.com",
"tags": ["production", "web"],
"api_key": "gmk_cru_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_aBcD"
},
"ingest_url": "https://app.glassmkr.com/api/v1/ingest",
"message": "Save your collector key. It will not be shown again."
} The collector key is shown once. Configure it on the agent before the dashboard tile leaves "pending first snapshot". The Idempotency-Key header is supported (24h replay window).
List servers
/servers AuthenticatedList all servers in the account.
Query parameters:
| Param | Type | Description |
|---|---|---|
tag | string | Filter by tag. Repeat for multiple tags (AND logic). |
limit | int | Page size, 1-100 (default 100). |
cursor | string | Opaque pagination cursor returned as next_cursor on the previous page. |
Response (200):
{
"servers": [
{
"id": "srv_a1b2c3d4",
"name": "web-prod-01",
"hostname": "web-prod-01.example.com",
"ip": "10.0.1.42",
"os_type": "ubuntu",
"os_version": "24.04 LTS",
"status": "active",
"suspended_at": null,
"suspended_reason": null,
"last_seen_at": "2026-05-09T07:00:00Z",
"collector_version": "0.13.3",
"active_alerts": 0,
"disk_health_rollup": "healthy",
"created_at": "2026-04-05T10:00:00Z",
"tags": ["production", "web"],
"dmi_vendor": "GIGABYTE",
"dmi_product": "R292-4S1-00",
"ipmi_sensors_count": 106
}
],
"next_cursor": null
} Per-snapshot metrics (CPU usage, RAM usage, disk usage) are not on the list endpoint. Use GET /servers/:id/health for the latest snapshot from a specific server.
status is active for normal operation, suspended when the server is disabled (see Billing). disk_health_rollup is the worst per-drive state across all SMART-monitored drives: healthy, declining, failing, or broken.
Get server
/servers/:server_id AuthenticatedGet full details for a single server. Same shape as the list endpoint plus a few read-only fields (config_overrides, free_analysis_used).
Update server
/servers/:server_id AuthenticatedUpdate name or tags. hostname is intentionally not updatable so ops can find a box by hostname after a rename.
{
"name": "web-prod-renamed",
"tags": ["production", "web", "fra1"]
}Delete server
/servers/:server_id?confirm=true AuthenticatedRemove a server and all its stored metrics. Irreversible. ?confirm=true is required; a bare DELETE returns 400.
Per-endpoint sub-limit: 100 deletes/hour/account.
Rotate collector key
/servers/:server_id/rotate-key AuthenticatedIssue a fresh collector key for an existing server. The previous key stops working immediately. Update /etc/glassmkr/collector.yaml on the agent host and restart the service before the next ingest cycle to avoid a gap.
{
"success": true,
"server": { "id": "srv_a1b2c3d4" },
"collector_key": "gmk_cru_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_aBcD",
"rotated_at": "2026-05-09T07:30:00Z",
"message": "Save this collector key. It will not be shown again."
} Rate-limited to 10/hour/account. Note: the field name on this endpoint is collector_key, not api_key as on POST /servers.
Restore server
/servers/:server_id/restore AuthenticatedRestore a single suspended server. Used when a server was disabled because no payment method was on file. Requires a card on file at call time (unless the account is exempt).
Error (400) when no card is on file: { "error": "no_card_on_file", "message": "Add a payment method in Settings to restore this server." }.
Restore all suspended servers
/servers/restore-all AuthenticatedBulk-restore every server suspended for no_card_on_file. Used by the dashboard's Settings → Disabled servers → Restore all button.
#Ingest
Push snapshot
/ingest Collector keySubmit a Crucible snapshot. Called by the agent every collection interval (default 60 seconds for Crucible v0.10.0+). Authenticated by the collector key in the Authorization: Bearer gmk_cru_live_... header. Rate-limited to one ingest per server per 55 seconds; subsequent calls return 429.
Request body (abbreviated; the agent emits the full Snapshot type):
{
"system": { "hostname": "web-prod-01", "ip": "10.0.1.42",
"os": "Ubuntu 24.04 LTS", "os_id": "ubuntu",
"kernel": "6.8.0-31-generic", "uptime_seconds": 86400 },
"cpu": { "user_percent": 15.2, "system_percent": 5.3,
"iowait_percent": 1.1, "idle_percent": 78.4,
"load_1m": 0.4, "load_5m": 0.6, "load_15m": 0.5,
"cores": [{ "core": 0, "user_percent": 20.1, "system_percent": 4.2,
"iowait_percent": 0.5, "idle_percent": 75.2 }] },
"memory": { "total_mb": 65536, "used_mb": 44032,
"available_mb": 21504, "swap_total_mb": 8192,
"swap_used_mb": 0 },
"disks": [{ "device": "/dev/nvme0n1p2", "mount": "/",
"total_gb": 500, "used_gb": 225, "available_gb": 250,
"percent_used": 47, "fstype": "ext4",
"io_read_mb_s": 15.2, "io_write_mb_s": 3.8,
"latency_p99_ms": 0.4,
"inodes_total": 32768000, "inodes_used": 1245000 }],
"smart": [{ "device": "/dev/nvme0n1", "model": "Samsung 990 Pro 2TB",
"health": "PASSED", "temperature_c": 38,
"percentage_used": 12, "power_on_hours": 8760 }],
"network": [{ "interface": "eth0", "speed_mbps": 10000,
"rx_bytes_sec": 125000, "tx_bytes_sec": 42000,
"rx_errors": 0, "tx_errors": 0,
"rx_drops": 0, "tx_drops": 0 }],
"raid": [],
"ipmi": { "available": true, "sel_entries_count": 12,
"ecc_errors": { "correctable": 0, "uncorrectable": 0 },
"sensors": [{ "name": "CPU1_TEMP", "value": 52, "unit": "C",
"status": "ok", "type": "temperature",
"upper_critical": 90 }] },
"os_alerts": { "oom_kills_recent": 0, "zombie_processes": 0,
"time_drift_ms": 0 },
"thermal": { "available": true, "source": "hwmon coretemp Package id 0",
"max_cpu_celsius": 52,
"cpu_readings": [{ "chip": "coretemp-isa-0000",
"label": "Package id 0", "celsius": 52 }] },
"dmi": { "available": true, "vendor": "supermicro",
"raw_vendor": "Supermicro Inc.",
"product_name": "SYS-1029P-WTR",
"bios_version": "3.4", "bios_date": "2023-01-12",
"is_virtual": false },
"gpu": { "available": true, "tier": "nvidia-smi",
"devices": [{ "index": 0, "name": "NVIDIA L4",
"temperature_c": 48, "utilization_percent": 12,
"memory_used_mb": 4096, "memory_total_mb": 24564 }] },
"collector_version": "0.13.3",
"timestamp": "2026-05-22T07:00:00Z"
} Optional top-level blocks: security, zfs, io_errors, io_latency, conntrack, systemd, ntp, file_descriptors, thermal, dmi, gpu, expected_reboot. Unknown fields are accepted via passthrough; new collector versions can extend the schema without a coupled Dashboard release.
Response (200):
{
"success": true,
"received_at": "2026-05-22T07:00:00.123Z",
"new_alerts": 0,
"active_alerts": 0
}#Health
Get server health
/servers/:server_id/health AuthenticatedCurrent health status and latest metric values for a server.
{
"server_id": "srv_a1b2c3d4",
"status": "healthy",
"last_seen": "2026-04-05T10:05:00Z",
"current": {
"cpu_percent": 21.6,
"ram_percent": 67.2,
"swap_used_mb": 0,
"disk_max_percent": 45.0,
"network_rx_mbps": 120.5,
"network_tx_mbps": 40.2,
"cpu_temp_c": 52,
"active_alerts": 0
}
}Get health history
/servers/:server_id/health/history AuthenticatedTime-series metric data.
| Param | Type | Description |
|---|---|---|
metric | string | cpu, memory, disk, network, temperature. |
from | ISO 8601 | Start time (default 1 hour ago). |
to | ISO 8601 | End time (default now). |
resolution | string | 1m, 5m, 1h, 1d (auto if omitted). |
Get server alerts
/servers/:server_id/health/alerts AuthenticatedList active and historical alerts. Query params: status (active, resolved, all), severity (critical, warning, info), from, to, page.
#Channels
Create channel
/channels Pro (Admin)Create a notification channel. Supported types: email, telegram, slack, webhook.
{
"name": "ops-telegram",
"type": "telegram",
"config": {
"bot_token": "7123456789:AAH1bGciOiJSUzI1NiIs",
"chat_id": "-1001234567890"
}
} Email config takes recipients, Slack takes webhook_url, webhook takes url and optional secret.
List channels
/channels AuthenticatedGet channel
/channels/:channel_id AuthenticatedSensitive fields like bot tokens are partially masked in GET responses.
Update channel
/channels/:channel_id Pro (Admin)Delete channel
/channels/:channel_id ProTest channel
/channels/:channel_id/test ProSend a test notification through the channel. Error (502) on delivery failure with the upstream error message.
#Alerts
Acknowledge alert
/alerts/:alert_id/acknowledge ProSilence notifications for the current occurrence; does not disable the rule. Event-type alerts (e.g. unexpected_reboot) auto-clear acknowledgement when a new occurrence stacks onto the card.
Resolve alert
/alerts/:alert_id/resolve ProManually resolve an alert without waiting for the underlying condition to clear. Mostly used for event-type alerts (24-hour TTL otherwise) and for force-clearing stuck state alerts.
List muted rules
/servers/:server_id/mutes AuthenticatedUpdate muted rules
/servers/:server_id/mutes Pro (Admin)Replace the muted-rules list. Changes take effect on the next ingest cycle from the server. Pass an empty array to unmute all.
{ "muted_rules": ["disk_space_high", "cpu_iowait_high"] }#Billing
Billing status
/billing/status AuthenticatedReturns plan, billing-period bounds, payment-method state, and the count of servers currently disabled for missing card.
{
"plan": "pro",
"has_default_payment_method": true,
"current_period_end": "2026-06-09T00:00:00Z",
"cancel_at_period_end": false,
"billing_enforcement_exempt": false,
"active_server_count": 7,
"suspended_no_card_count": 0,
"free_server_quota": 3
} Pro customers without a payment method see has_default_payment_method: false. The grace period is the later of current_period_end and customer.created_at + 30 days; after that, servers beyond the free quota are suspended.
Other billing endpoints
POST /billing/checkout: create a Stripe Checkout session for plan upgrade.POST /billing/portal: create a Stripe Customer Portal session.POST /billing/resume: re-enable auto-renew on a cancelled subscription.POST /billing/downgrade: schedule a downgrade to Free at period end.
#Meta
Version
/version PublicReturns the latest published Crucible version and the minimum supported version.
{
"crucible": {
"latest": "0.13.3",
"min_supported": "0.7.0",
"changelog_url": "https://github.com/glassmkr/crucible/releases"
},
"dashboard": { "version": "1.0.0" }
} The latest value is sourced from the npm registry's @glassmkr/crucible latest dist-tag.
#Rate limits
Token-bucket limiter applied as four overlapping tiers (first failure wins; failures still cost a token on the per-IP debit so brute-force probing burns budget):
| Tier | Capacity | Refill | Applies to |
|---|---|---|---|
| Per-IP | 100 | 10/sec | Every request, including pre-auth. |
| Per-key | 1000 | 100/sec | Authenticated requests, scoped to one collector or account key. |
| Per-account | 5000 | 500/sec | All authenticated requests within one customer. |
| POST /servers | 100 | 100/hour | Server registration sub-limit. |
| DELETE /servers/:id | 100 | 100/hour | Deletion sub-limit. |
| POST /servers/:id/rotate-key | 10 | 10/hour | Key-rotation sub-limit. |
The ingest endpoint enforces a per-server soft limit of one push per 55 seconds (returns 429, separate from the token-bucket layer). When token-bucket-rate-limited, the API returns 429 Too Many Requests with a Retry-After header.
#Pagination
List endpoints (currently GET /servers) use opaque cursor pagination: pass ?limit= (1-100, default 100) and the previous response's next_cursor as ?cursor=. next_cursor is null on the final page.
#Idempotency
POST /servers honors an Idempotency-Key header (1-255 printable ASCII). The first response (success or deterministic 4xx) is cached for 24 hours; replays return the cached response with an Idempotency-Replayed: true header. Concurrent retries with the same key while the original is still in flight return 409.
Last verified: 2026-05-22 against Crucible v0.13.3 and Dashboard v1.0. For tier-gating details see /docs/api/tier-gating.