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

POST /auth/register Public

Create 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

POST /auth/login Public

Authenticate 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

POST /auth/logout Authenticated

Invalidate the current session token. Response (204) no content.

Get current user

GET /auth/me Authenticated

Returns 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

POST /auth/verify Public

Confirm an email address using the token from the verification email.

{ "token": "verify_abc123def456" }

#Servers

Register server

POST /servers Authenticated

Register 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

GET /servers Authenticated

List all servers in the account.

Query parameters:

ParamTypeDescription
tagstringFilter by tag. Repeat for multiple tags (AND logic).
limitintPage size, 1-100 (default 100).
cursorstringOpaque 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

GET /servers/:server_id Authenticated

Get 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

PATCH /servers/:server_id Authenticated

Update 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

DELETE /servers/:server_id?confirm=true Authenticated

Remove 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

POST /servers/:server_id/rotate-key Authenticated

Issue 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

POST /servers/:server_id/restore Authenticated

Restore 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

POST /servers/restore-all Authenticated

Bulk-restore every server suspended for no_card_on_file. Used by the dashboard's Settings → Disabled servers → Restore all button.

#Ingest

Push snapshot

POST /ingest Collector key

Submit 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

GET /servers/:server_id/health Authenticated

Current 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

GET /servers/:server_id/health/history Authenticated

Time-series metric data.

ParamTypeDescription
metricstringcpu, memory, disk, network, temperature.
fromISO 8601Start time (default 1 hour ago).
toISO 8601End time (default now).
resolutionstring1m, 5m, 1h, 1d (auto if omitted).

Get server alerts

GET /servers/:server_id/health/alerts Authenticated

List active and historical alerts. Query params: status (active, resolved, all), severity (critical, warning, info), from, to, page.

#Channels

Create channel

POST /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

GET /channels Authenticated

Get channel

GET /channels/:channel_id Authenticated

Sensitive fields like bot tokens are partially masked in GET responses.

Update channel

PUT /channels/:channel_id Pro (Admin)

Delete channel

DELETE /channels/:channel_id Pro

Test channel

POST /channels/:channel_id/test Pro

Send a test notification through the channel. Error (502) on delivery failure with the upstream error message.

#Alerts

Acknowledge alert

POST /alerts/:alert_id/acknowledge Pro

Silence 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

POST /alerts/:alert_id/resolve Pro

Manually 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

GET /servers/:server_id/mutes Authenticated

Update muted rules

PUT /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

GET /billing/status Authenticated

Returns 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

GET /version Public

Returns 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):

TierCapacityRefillApplies to
Per-IP10010/secEvery request, including pre-auth.
Per-key1000100/secAuthenticated requests, scoped to one collector or account key.
Per-account5000500/secAll authenticated requests within one customer.
POST /servers100100/hourServer registration sub-limit.
DELETE /servers/:id100100/hourDeletion sub-limit.
POST /servers/:id/rotate-key1010/hourKey-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.