# Admin

> Platform administration (platform admins only), including the AI ops kill switch.

Platform administration (platform admins only), including the AI ops kill switch.

| Operation | Method and path |
| --- | --- |
| [List waitlist entries](#list-waitlist-entries) | `GET /v1/admin/waitlist` |
| [Erase a waitlist entry](#erase-a-waitlist-entry) | `DELETE /v1/admin/waitlist/{entry_id}` |
| [List platform invitations](#list-platform-invitations) | `GET /v1/admin/invites` |
| [Create a platform invitation](#create-a-platform-invitation) | `POST /v1/admin/invites` |
| [Revoke a platform invitation](#revoke-a-platform-invitation) | `POST /v1/admin/invites/{invite_id}/revoke` |
| [AI ops kill switch status](#ai-ops-kill-switch-status) | `GET /v1/admin/ai-ops/kill-switch` |
| [Turn the kill switch on (agents become read-only, agent tokens revoked)](#turn-the-kill-switch-on-agents-become-read-only-agent-tokens-revoked) | `POST /v1/admin/ai-ops/kill-switch/engage` |
| [Turn the kill switch off (passkey required)](#turn-the-kill-switch-off-passkey-required) | `POST /v1/admin/ai-ops/kill-switch/disengage` |
| [Platform change log (AI ops tool calls and decisions)](#platform-change-log-ai-ops-tool-calls-and-decisions) | `GET /v1/admin/changes` |
| [Get a change](#get-a-change) | `GET /v1/admin/changes/{change_id}` |

## List waitlist entries

```http
GET /v1/admin/waitlist
```

- **Operation:** `listWaitlist`
- **Scopes:** `admin:platform`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `status` | query | `pending`, `confirmed`, `invited`, `joined` |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of waitlist entries. | [WaitlistEntryList](/docs/api/objects/waitlist-entry-list/) |

**Errors:** `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/admin/waitlist \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "wle_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "email": "ada@example.co.uk",
      "name": "string",
      "building": "string",
      "heard_from": "string",
      "status": "pending",
      "consent_version": "string",
      "consented_at": "2026-10-22T13:05:00Z",
      "confirmed_at": "2026-10-22T13:05:00Z",
      "invited_at": "2026-10-22T13:05:00Z",
      "joined_at": "2026-10-22T13:05:00Z",
      "created_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Erase a waitlist entry

```http
DELETE /v1/admin/waitlist/{entry_id}
```

- **Operation:** `deleteWaitlistEntry`
- **Scopes:** `admin:platform`
- **Idempotency-Key:** required

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `entry_id` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `204` No Content | Entry erased. | none |

**Errors:** `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X DELETE https://api.guv.dev/v1/admin/waitlist/ent_01jbx2m9k7q4r8t5v6w3y0z1pr \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

## List platform invitations

```http
GET /v1/admin/invites
```

- **Operation:** `listInvites`
- **Scopes:** `admin:platform`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `status` | query | `active`, `used`, `expired`, `revoked` |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of invitations. | [InviteList](/docs/api/objects/invite-list/) |

**Errors:** `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/admin/invites \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "sgi_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "email": "ada@example.co.uk",
      "waitlist_entry_id": "wle_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "note": "string",
      "status": "active",
      "code_hint": "string",
      "created_at": "2026-10-22T13:05:00Z",
      "expires_at": "2026-10-22T13:05:00Z",
      "accepted_at": "2026-10-22T13:05:00Z",
      "used_at": "2026-10-22T13:05:00Z",
      "used_by_user_id": "string",
      "revoked_at": "2026-10-22T13:05:00Z",
      "created_by_user_id": "string"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Create a platform invitation

```http
POST /v1/admin/invites
```

- **Operation:** `createInvite`
- **Scopes:** `admin:platform`
- **Idempotency-Key:** required

**Request body** (`application/json`, required): [InviteCreateRequest](/docs/api/objects/invite-create-request/)

| Field | Type | Description |
| --- | --- | --- |
| `email` | string (email) |  |
| `waitlist_entry_id` | string |  |
| `expires_in_days` | integer | Default `14`. |
| `note` | string |  |
| `send_email` | boolean | Default `true`. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `201` Created | Invitation created. The code is shown once. | [InviteCreated](/docs/api/objects/invite-created/) |

**Errors:** `400`, `401`, `403`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/admin/invites \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "ada@example.co.uk",
  "waitlist_entry_id": "wle_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "expires_in_days": 14,
  "note": "string",
  "send_email": true
}'
```

**Example response**

```json
{
  "id": "sgi_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "email": "ada@example.co.uk",
  "waitlist_entry_id": "wle_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "note": "string",
  "status": "active",
  "code_hint": "string",
  "created_at": "2026-10-22T13:05:00Z",
  "expires_at": "2026-10-22T13:05:00Z",
  "accepted_at": "2026-10-22T13:05:00Z",
  "used_at": "2026-10-22T13:05:00Z",
  "used_by_user_id": "string",
  "revoked_at": "2026-10-22T13:05:00Z",
  "created_by_user_id": "string",
  "code": "string",
  "url": "https://guv.dev/"
}
```

## Revoke a platform invitation

```http
POST /v1/admin/invites/{invite_id}/revoke
```

- **Operation:** `revokeInvite`
- **Scopes:** `admin:platform`
- **Idempotency-Key:** required

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `invite_id` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Invitation revoked. | [Invite](/docs/api/objects/invite/) |

**Errors:** `401`, `403`, `409`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/admin/invites/inv_01jbx2m9k7q4r8t5v6w3y0z1pr/revoke \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

**Example response**

```json
{
  "id": "sgi_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "email": "ada@example.co.uk",
  "waitlist_entry_id": "wle_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "note": "string",
  "status": "active",
  "code_hint": "string",
  "created_at": "2026-10-22T13:05:00Z",
  "expires_at": "2026-10-22T13:05:00Z",
  "accepted_at": "2026-10-22T13:05:00Z",
  "used_at": "2026-10-22T13:05:00Z",
  "used_by_user_id": "string",
  "revoked_at": "2026-10-22T13:05:00Z",
  "created_by_user_id": "string"
}
```

## AI ops kill switch status

```http
GET /v1/admin/ai-ops/kill-switch
```

- **Operation:** `getKillSwitch`
- **Scopes:** `admin:platform`

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [KillSwitchStatus](/docs/api/objects/kill-switch-status/) |

**Errors:** `400`, `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/admin/ai-ops/kill-switch \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "engaged": false,
  "agents_read_only": false,
  "source": "dns_txt",
  "reason": "string",
  "engaged_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "engaged_at": "2026-10-22T13:05:00Z",
  "signature_valid": false,
  "checked_at": "2026-10-22T13:05:00Z"
}
```

## Turn the kill switch on (agents become read-only, agent tokens revoked)

```http
POST /v1/admin/ai-ops/kill-switch/engage
```

Platform admins, the Auditor agent and anomaly detectors may engage. Nobody but a platform admin with a passkey may disengage.

- **Operation:** `engageKillSwitch`
- **Scopes:** `admin:platform`
- **Idempotency-Key:** required

**Request body** (`application/json`, required): [KillSwitchEngage](/docs/api/objects/kill-switch-engage/)

| Field | Type | Description |
| --- | --- | --- |
| `reason` * | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [KillSwitchStatus](/docs/api/objects/kill-switch-status/) |

**Errors:** `400`, `401`, `403`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/admin/ai-ops/kill-switch/engage \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "string"
}'
```

**Example response**

```json
{
  "engaged": false,
  "agents_read_only": false,
  "source": "dns_txt",
  "reason": "string",
  "engaged_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "engaged_at": "2026-10-22T13:05:00Z",
  "signature_valid": false,
  "checked_at": "2026-10-22T13:05:00Z"
}
```

## Turn the kill switch off (passkey required)

```http
POST /v1/admin/ai-ops/kill-switch/disengage
```

- **Operation:** `disengageKillSwitch`
- **Scopes:** `admin:platform`
- **Idempotency-Key:** required

**Request body** (`application/json`, required): [KillSwitchDisengage](/docs/api/objects/kill-switch-disengage/)

| Field | Type | Description |
| --- | --- | --- |
| `reason` * | string |  |
| `assertion` * | [PasskeyAssertion](/docs/api/objects/passkey-assertion/) |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [KillSwitchStatus](/docs/api/objects/kill-switch-status/) |

**Errors:** `400`, `401`, `403`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/admin/ai-ops/kill-switch/disengage \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "string",
  "assertion": {
    "challenge_id": "string",
    "credential": {
      "id": "string",
      "rawId": "string",
      "type": "public-key",
      "response": {},
      "authenticatorAttachment": "string",
      "clientExtensionResults": {}
    }
  }
}'
```

**Example response**

```json
{
  "engaged": false,
  "agents_read_only": false,
  "source": "dns_txt",
  "reason": "string",
  "engaged_by": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "engaged_at": "2026-10-22T13:05:00Z",
  "signature_valid": false,
  "checked_at": "2026-10-22T13:05:00Z"
}
```

## Platform change log (AI ops tool calls and decisions)

```http
GET /v1/admin/changes
```

- **Operation:** `listChanges`
- **Scopes:** `admin:platform`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `status` | query | string |  |
| `tier` | query | integer |  |
| `incident` | query | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of Change objects. | [ChangeList](/docs/api/objects/change-list/) |

**Errors:** `400`, `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/admin/changes \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "chg_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "actor": {
        "type": "agent",
        "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
        "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
        "via_agent": {
          "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
          "name": "claude-code-on-stewart-laptop"
        },
        "on_behalf_of": {
          "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
          "display": "Stewart Webb"
        }
      },
      "tool": "restart_deployment",
      "tier": 0,
      "target": {
        "type": "service",
        "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
        "name": "web",
        "url": "/v1/projects/primecarers/services/web"
      },
      "cell": "string",
      "input": {},
      "plan": {
        "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
        "dry_run": true,
        "summary": "1 to update. +£5.00/month at current usage.",
        "changes": [
          {
            "action": "update",
            "resource": {
              "type": "service",
              "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
              "name": "web"
            },
            "environment": "production",
            "summary": "Raise web memory limit from 1 GB to 2 GB",
            "diff": [
              {
                "path": "/resources/memory_mb",
                "before": 1024,
                "after": 2048
              }
            ],
            "destructive": false,
            "requires_approval": true,
            "reason": "production requires approval for changes made by agents"
          }
        ],
        "cost": {
          "current_monthly": {
            "amount": "18.20",
            "currency": "GBP",
            "display": "£18.20"
          },
          "projected_monthly": {
            "amount": "23.20",
            "currency": "GBP",
            "display": "£23.20"
          },
          "delta_monthly": {
            "amount": "5.00",
            "currency": "GBP",
            "display": "£5.00"
          },
          "confidence": "medium"
        },
        "policy": {
          "decision": "approval_required",
          "reasons": [
            {
              "code": "environment_protected",
              "message": "production requires approval for changes made by agents",
              "policy": "guv.environment.protection"
            }
          ],
          "approver_roles": [
            "owner",
            "admin"
          ]
        },
        "requires_approval": true,
        "expires_at": "2026-10-23T15:00:00Z"
      },
      "policy": {
        "decision": "allow",
        "reasons": [
          {}
        ],
        "approver_roles": [
          "owner"
        ],
        "tier": 0
      },
      "reviewer": {
        "verdict": "approve",
        "model": "string",
        "notes": "string"
      },
      "approval_id": "string",
      "operation_id": "string",
      "incident_id": "string",
      "status": "proposed",
      "created_at": "2026-10-22T13:05:00Z",
      "finished_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Get a change

```http
GET /v1/admin/changes/{change_id}
```

- **Operation:** `getChange`
- **Scopes:** `admin:platform`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `change_id` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [Change](/docs/api/objects/change/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/admin/changes/cha_01jbx2m9k7q4r8t5v6w3y0z1pr \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "id": "chg_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "actor": {
    "type": "agent",
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "display": "Stewart Webb via agent claude-code-on-stewart-laptop",
    "via_agent": {
      "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
      "name": "claude-code-on-stewart-laptop"
    },
    "on_behalf_of": {
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    }
  },
  "tool": "restart_deployment",
  "tier": 0,
  "target": {
    "type": "service",
    "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
    "name": "web",
    "url": "/v1/projects/primecarers/services/web"
  },
  "cell": "string",
  "input": {},
  "plan": {
    "plan_hash": "sha256:9f2c1e0b7a4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a49",
    "dry_run": true,
    "summary": "1 to update. +£5.00/month at current usage.",
    "changes": [
      {
        "action": "update",
        "resource": {
          "type": "service",
          "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv",
          "name": "web"
        },
        "environment": "production",
        "summary": "Raise web memory limit from 1 GB to 2 GB",
        "diff": [
          {
            "path": "/resources/memory_mb",
            "before": 1024,
            "after": 2048
          }
        ],
        "destructive": false,
        "requires_approval": true,
        "reason": "production requires approval for changes made by agents"
      }
    ],
    "cost": {
      "current_monthly": {
        "amount": "18.20",
        "currency": "GBP",
        "display": "£18.20"
      },
      "projected_monthly": {
        "amount": "23.20",
        "currency": "GBP",
        "display": "£23.20"
      },
      "delta_monthly": {
        "amount": "5.00",
        "currency": "GBP",
        "display": "£5.00"
      },
      "confidence": "medium"
    },
    "policy": {
      "decision": "approval_required",
      "reasons": [
        {
          "code": "environment_protected",
          "message": "production requires approval for changes made by agents",
          "policy": "guv.environment.protection"
        }
      ],
      "approver_roles": [
        "owner",
        "admin"
      ]
    },
    "requires_approval": true,
    "expires_at": "2026-10-23T15:00:00Z"
  },
  "policy": {
    "decision": "allow",
    "reasons": [
      {
        "code": "environment_protected",
        "message": "production requires approval for changes made by agents",
        "policy": "guv.environment.protection"
      }
    ],
    "approver_roles": [
      "owner"
    ],
    "tier": 0
  },
  "reviewer": {
    "verdict": "approve",
    "model": "string",
    "notes": "string"
  },
  "approval_id": "string",
  "operation_id": "string",
  "incident_id": "string",
  "status": "proposed",
  "created_at": "2026-10-22T13:05:00Z",
  "finished_at": "2026-10-22T13:05:00Z"
}
```
