---
title: "Smoke-check the admin authentication gate"
method: GET
path: "/api/v1/admin/ping"
tags: ["Admin"]
---

# Smoke-check the admin authentication gate

`GET /api/v1/admin/ping`

## Request

- Base URL: `https://api.uptimemonitoring.com`
- URL: `https://api.uptimemonitoring.com/api/v1/admin/ping`
- Auth: HTTP bearer

Returns the AUTHENTICATED CALLER's own GitHub identity along with
`admin: true` when (and only when) that caller (AuthInfo.UserID,
joined to `admins` via `users.github_id`) has a row in the
`admins` table (keyed on the immutable GitHub numeric ID, see
migration 018). Used to confirm the admin gate is wired and
working — the same `auth.RequireAdmin` middleware composes
future admin-only routes (e.g., announcement CRUD, admin SSE).
Team accounts B1 (issue #323) made non-owner admins possible, so
this is NOT necessarily the identity of the account the request
happens to be scoped to — a viewer/member admin inspecting
another user's account sees their own identity here.

Responses split cleanly:
- **200** — caller is admin; payload includes the caller's own
  `github_id`/`github_username` (both `null` if the caller has
  no GitHub identity), and `admin: true`.
- **401** — produced by the upstream Bearer middleware when the
  request has no `Authorization` header or the token is
  invalid.
- **403** — opaque `{"error":"forbidden"}` when the request is
  authenticated but the account is not in the admins table.
  Same envelope on every non-admin path: do not leak whether
  the route requires admin or whether the caller happens to
  not be one.
- **429** — the global per-account rate limiter still applies
  to admin routes (200/min cap shared with the rest of
  `/api/v1`).

Admin membership is sourced from the immutable numeric GitHub
ID (`accounts.github_id`), not the mutable username. A renamed
admin retains access; a non-admin who happens to claim a freed
username does not inherit it.

## Response `200`

Caller is an admin.

- AdminPingResponse
  - `github_id` integer, nullable, required: GitHub numeric ID of the AUTHENTICATED CALLER (sourced from `users.github_id` for the user identified by AuthInfo.UserID) — never the account owner's. Team accounts B1 (issue #323) made non-owner admins possible, so a viewer/member admin inspecting someone else's account must see their own identity here, not the account's. Display-only — the gate itself keys on AuthInfo.UserID, not this field. `null` if the caller has no GitHub identity (an email- or Google-only user who is still an admin via their users.id).
  - `github_username` string, nullable, required: GitHub username of the authenticated caller, sourced the same way as github_id. Display-only; the gate does not key on this value because GitHub usernames are mutable. `null` under the same condition as github_id.
  - `admin` boolean, required: Always `true` on a 200 response.

## Other responses

- `401`: Missing or invalid credentials.
- `403`: Authenticated but not an admin. Body is the opaque `{"error":"forbidden"}` envelope; same shape used for every admin-gated 403 across the API.
- `429`: Per-account dispatcher rate limit exceeded (200 requests/minute). The `Retry-After` header gives the integer seconds until at least one token has refilled. The `X-RateLimit-*` trio reflects the bucket whose rejection produced this response — usually the global 200/min cap; on `POST /api/v1/monitors` a 429 may instead reflect the per-route 20/min creation limit.
- `500`: Lookup against the `admins` table failed. The middleware never fails open: a transient DB error returns 500 rather than letting a non-admin reach an admin-only handler.

---

[API](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api.md) · [All operations](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api/llms.txt) · [OpenAPI document](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api/revisions/ddcb2a8bc70f?raw)
