# Programmatic API + account API keys

For the full endpoint reference see the [API reference](/docs/api); for tier gating see [/docs/api/tier-gating](/docs/api/tier-gating). The full read+write programmatic API is on every plan, bounded only by the 3-node quota and the rate limits.

## The two key types

| Key type | Prefix | Used for | Created via |
| --- | --- | --- | --- |
| **Account API key** | `gmk_acct_live_` | Management endpoints: list servers, create servers, rotate keys, query audit log. | `POST /api/v1/account/keys` via curl / Ansible / Terraform after recent re-authentication via `POST /api/v1/account/verify-password`. Available on every plan. |
| **Collector key** | `gmk_cru_live_` | Telemetry ingestion only. Scoped to one server. Cannot list other servers or read account settings. | Returned by `POST /api/v1/servers`. Rotate via `POST /api/v1/servers/{id}/rotate-key`. |

Older agents may still have `col_*` collector keys. Both formats authenticate against `/api/v1/ingest`; you can rotate at your own pace.

## Quickstart: provision 50 servers in two minutes

### 1. Create an account API key

Two calls. Use a logged-in browser session (cookie jar). Account-key creation cannot use another account key.

```
# 1. Re-authenticate (opens a 5-minute step-up window).
curl -sS -X POST https://app.glassmkr.com/api/v1/account/verify-password \
  -b session.cookie \
  -H "Content-Type: application/json" \
  -d '{"password":"your-dashboard-password"}'

# 2. Create the key (returned in plaintext exactly once; save it).
#    scope must be "write" to create servers ("write" is the default).
curl -sS -X POST https://app.glassmkr.com/api/v1/account/keys \
  -b session.cookie \
  -H "Content-Type: application/json" \
  -d '{"name":"ansible-prod","scope":"write"}'
```

Server creation is capped at your plan's node quota (3 on Free; your subscribed count on Pro), so a 50-server run assumes a Pro plan.

### 2. Create the servers via curl

```
API_KEY="gmk_acct_live_..."

for i in $(seq 1 50); do
  RESPONSE=$(curl -sS -X POST https://app.glassmkr.com/api/v1/servers \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: bootstrap-$(date +%s)-$i" \
    -d '{"name":"web-'$i'","hostname":"web-'$i'.prod.example.com","tags":["prod","web"]}')

  COLLECTOR_KEY=$(echo "$RESPONSE" | jq -r '.server.api_key')
  echo "web-$i  =>  $COLLECTOR_KEY"
done
```

### 3. Provision the agent on each host

```
curl -sf https://glassmkr.com/install.sh | sudo GLASSMKR_API_KEY=$COLLECTOR_KEY bash
```

## Rate limits

Three default tiers (per-IP, per-key, per-account) plus per-endpoint sub-limits for the high-risk operations:

| Tier | Capacity | Refill |
| --- | --- | --- |
| Per-IP | 100 burst | 10/sec |
| Per-key | 1000 burst | 100/sec |
| Per-account | 5000 burst | 500/sec |
| `POST /servers` | 100 / hour / account | n/a |
| `DELETE /servers/{id}` | 100 / hour / account | n/a |
| `POST /servers/{id}/rotate-key` | 10 / hour / account | n/a |
| `POST /account/keys` | 10 / hour / account | n/a |
| `POST /account/keys/{id}/rotate` | 10 / hour / account | n/a |

On 429, the response body includes `tier` and `retry_after_seconds`; the `Retry-After` header is set.

## Idempotency

`POST /api/v1/servers` accepts an `Idempotency-Key` header (Stripe-style). Retries within 24h with the same key return the cached response (status + body). Use one for every retried server-creation operation in your automation.

## Step-up authentication

API key creation and rotation require recent password re-verification. POST your current password to `/api/v1/account/verify-password` (session auth, not bearer-token) to stamp `last_password_verified_at`. After that, sensitive operations succeed for 5 minutes.

This protects against session-stealing attacks: an attacker with a leaked cookie cannot mint a long-lived API key without also knowing the password.

## Audit log

Every API call writes one row to the audit log. Read it via `GET /api/v1/account/audit`:

- Paginated by ts cursor (`?limit=50&cursor=...`)
- Filterable by `key_id`, `resource_type`, `resource_id`, `action`, `result`
- Plan-based retention: 365 days for Pro, 30 days for Free
- Append-only: we cannot edit history server-side

## Securing your keys

- Store in your secret manager (1Password, Vault, AWS Secrets Manager, Doppler). Never in `.env` committed to git.
- Use a separate key per integration (Ansible, CI, Terraform). Revoking one does not disrupt the others.
- Set an `expires_at` on short-lived CI keys.
- If a key leaks: revoke immediately at `DELETE /api/v1/account/keys/{id}` or via the dashboard.
- GitHub secret-scanning partner registration for `gmk_acct_live_` and `gmk_cru_live_` prefixes is queued; once active, accidentally-committed keys auto-revoke.

## Errors

Standard envelope:

```
{
  "error": "machine_readable_code",
  "message": "Human-readable explanation",
  "request_id": "req_abc123",
  "documentation_url": "https://glassmkr.com/docs/api/errors/{code}"
}
```

Use `request_id` when contacting [support](mailto:support@glassmkr.com); we correlate it against the audit log and application logs.

Last verified: 2026-06-26 against the live API.
