# AdminService

**Spinbox is experimental: APIs change and data may be lost.** What is built and what proves it is on [Status](https://spin-stack.github.io/website/reference/status.md); what Spinbox does not do is on [Limitations](https://spin-stack.github.io/website/overview/limitations.md).

> spin.v1.AdminService: each procedure, its request and its response.

<!-- Generated by tools/refgen from spin:internal/controlplane/apidocs/spin.v1.openapi.json. Edit the source, not this file. -->

AdminService manages installation-wide settings, user management, and resource presets. Only accessible to instance administrators.

## AddAllowedEmail

`POST /spin.v1.AdminService/AddAllowedEmail`

Add allowed email/domain

**Request**

| Field | Type | What it is |
|---|---|---|
| `pattern` | string | Email address (user@example.com) or domain (example.com) |
| `type` | [AllowedEmailType](https://spin-stack.github.io/website/reference/api/types.md#allowedemailtype) |  |
| `description` | string | Optional description |

**Response**

| Field | Type | What it is |
|---|---|---|
| `entry` | [AllowedEmail](https://spin-stack.github.io/website/reference/api/types.md#allowedemail) |  |

## CreateNetworkPolicy

`POST /spin.v1.AdminService/CreateNetworkPolicy`

**Request**

| Field | Type | What it is |
|---|---|---|
| `policy` | [NetworkPolicyRule](https://spin-stack.github.io/website/reference/api/types.md#networkpolicyrule) |  |

**Response**

No fields: `{}`.

## CreateResourcePreset

`POST /spin.v1.AdminService/CreateResourcePreset`

Create a resource preset

**Request**

| Field | Type | What it is |
|---|---|---|
| `name` | string | Required: display name |
| `description` | string | Optional: description |
| `cpuCores` | integer (int32) | Required: CPU cores |
| `memoryMb` | integer (int32) | Required: memory in MB |
| `diskGb` | integer (int32) | Required: disk in GB |
| `burst` | [BurstMode](https://spin-stack.github.io/website/reference/api/types.md#burstmode) | Required |
| `isDefault` | boolean | Optional: make this the default |
| `enabled` | boolean | Optional: enable the preset (default true) |
| `sortOrder` | integer (int32) | Optional: display order |
| `burstFloorCpuCores` | integer (int32) | Required for a burst mode: what its workspaces start with, at most the preset's size. |
| `burstFloorMemoryMb` | integer (int32) |  |
| `bandwidthMbps` | integer (int32) | What a workspace of it may carry each way, in megabits a second; 0 is unshaped. |
| `rpoSeconds` | integer (int32) | How far behind the object store a workspace of it may fall, in seconds; 0 is the installation's. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `preset` | [ResourcePreset](https://spin-stack.github.io/website/reference/api/types.md#resourcepreset) |  |

## DecideConfigChange

`POST /spin.v1.AdminService/DecideConfigChange`

Apply a change the file proposes, or dismiss it. Either is remembered until the file says something else again. Audited with the admin who decided.

**Request**

| Field | Type | What it is |
|---|---|---|
| `kind` | [ConfigItemKind](https://spin-stack.github.io/website/reference/api/types.md#configitemkind) |  |
| `name` | string |  |
| `says` | string | ConfigChange.says, as listed. |
| `apply` | boolean | True writes what the file says; false keeps what the installation has. |

**Response**

No fields: `{}`.

## DeleteNetworkPolicy

`POST /spin.v1.AdminService/DeleteNetworkPolicy`

Refused while a workspace runs under the policy or it is the installation's default.

**Request**

| Field | Type | What it is |
|---|---|---|
| `name` | string |  |

**Response**

No fields: `{}`.

## DeleteResourcePreset

`POST /spin.v1.AdminService/DeleteResourcePreset`

Delete a resource preset

**Request**

| Field | Type | What it is |
|---|---|---|
| `id` | string | Required: preset ID to delete |

**Response**

No fields: `{}`.

## ExplainStorageKey

`POST /spin.v1.AdminService/ExplainStorageKey`

What one key in the store is, and whose: a key read off the bucket's console says nothing a person can act on.

**Request**

| Field | Type | What it is |
|---|---|---|
| `key` | string |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `what` | string | What the object is, in words. |
| `volume` | [StoredVolume](https://spin-stack.github.io/website/reference/api/types.md#storedvolume) | Whose it is, when it is a volume's. |

## GetBill

`POST /spin.v1.AdminService/GetBill`

What the installation cost in a month, as its cloud's bill says: by day and service, what the month is forecast to end at, and its budget. As the control plane last read the bill - a few times a day (internal/controlplane/domain/bill) - never the cloud itself, where every question to its billing is billed.

**Request**

| Field | Type | What it is |
|---|---|---|
| `month` | `Timestamp` | The month, as any instant in it (UTC's); unset is the month in progress. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `month` | `Timestamp` | The month's first instant, UTC. |
| `readAt` | `Timestamp` | When the bill was last read; unset where it never was - no scope (bill_scope), or a cloud that sends none - and then nothing below is known. |
| `currency` | string | ISO 4217; every amount here is in millionths of it. |
| `totalMicros` | string (int64) | The month so far, every day and service. |
| `forecastMicros` | string or null (int64) | What the month is forecast to end at; unset where the cloud has no forecast yet. |
| `budgetLimitMicros` | string or null (int64) | The installation's budget and what the cloud counts against it; unset where it has none. |
| `budgetActualMicros` | string or null (int64) |  |
| `budgetForecastMicros` | string or null (int64) |  |
| `days` | list of [BillDay](https://spin-stack.github.io/website/reference/api/types.md#billday) | Oldest first. |

## GetCosts

`POST /spin.v1.AdminService/GetCosts`

A month of the bill shared out to the workspaces that caused it: the runners' by what each workspace held of them, the volumes' bucket by what its volumes kept. What nobody caused is said apart and never spread: the runners' capacity nobody held, the bucket's bytes no workspace keeps, and the platform's own cost by service (internal/controlplane/domain/finops).

**Request**

| Field | Type | What it is |
|---|---|---|
| `month` | `Timestamp` | The month, as any instant in it (UTC's); unset is the month in progress. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `month` | `Timestamp` | The month's first instant, UTC. |
| `currency` | string | ISO 4217; every amount here is in millionths of it. |
| `workspaces` | list of [WorkspaceCost](https://spin-stack.github.io/website/reference/api/types.md#workspacecost) | Costliest first. |
| `idleMicros` | string (int64) | What the runners cost that no workspace held. |
| `unstoredMicros` | string (int64) | What the volumes' bucket cost for bytes no workspace keeps: the images, what nothing references any more. |
| `platform` | list of [ServiceCost](https://spin-stack.github.io/website/reference/api/types.md#servicecost) | Every other cost - the control plane, the proxy, keys, logs - by service, costliest first. |

## GetFleetVersions

`POST /spin.v1.AdminService/GetFleetVersions`

What is running where: this control plane, each host's runner and machine, and the extension releases workspaces are on. An installation is updated in halves, and this is the one page that says which halves it is in.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `controlPlaneVersion` | string |  |
| `machineVersion` | string | The spin-machine release this control plane pins, which a host should be running. |
| `hosts` | list of [FleetHostVersion](https://spin-stack.github.io/website/reference/api/types.md#fleethostversion) |  |
| `extensions` | list of [ExtensionVersion](https://spin-stack.github.io/website/reference/api/types.md#extensionversion) |  |

## GetInstallationSettings

`POST /spin.v1.AdminService/GetInstallationSettings`

Get installation settings

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `settings` | [InstallationSettings](https://spin-stack.github.io/website/reference/api/types.md#installationsettings) |  |

## GetMonitoring

`POST /spin.v1.AdminService/GetMonitoring`

The Grafana the control plane serves over the installation's metrics, and the release's dashboards in it.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `grafana` | [Grafana](https://spin-stack.github.io/website/reference/api/types.md#grafana) | Unset where the control plane serves none: the installation keeps no metrics, or this machine has no Grafana. |

## GetPlatformImages

`POST /spin.v1.AdminService/GetPlatformImages`

Whether the base image and the supervisor this control plane's image carries are published.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `images` | list of [PlatformImage](https://spin-stack.github.io/website/reference/api/types.md#platformimage) | Empty until the object store is configured: there is nowhere to publish to. |
| `baseImages` | list of [BaseImage](https://spin-stack.github.io/website/reference/api/types.md#baseimage) | Every base image published, one per machine release, newest first. |
| `supervisorReleases` | list of [ExtensionRelease](https://spin-stack.github.io/website/reference/api/types.md#extensionrelease) | Every release of the supervisor, newest first. |
| `agentReleases` | list of [ExtensionRelease](https://spin-stack.github.io/website/reference/api/types.md#extensionrelease) | Every release of the agent CLIs (Claude Code and Codex, one extension), newest first. |

## GetRegistrationMode

`POST /spin.v1.AdminService/GetRegistrationMode`

Get registration mode (open, whitelist, closed)

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `mode` | [RegistrationMode](https://spin-stack.github.io/website/reference/api/types.md#registrationmode) |  |

## GetSecuritySettings

`POST /spin.v1.AdminService/GetSecuritySettings`

Get security settings

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `settings` | [SecuritySettings](https://spin-stack.github.io/website/reference/api/types.md#securitysettings) |  |

## GetStorageSettings

`POST /spin.v1.AdminService/GetStorageSettings`

Where the installation's object store is. The secret is never read back.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `settings` | [StorageSettings](https://spin-stack.github.io/website/reference/api/types.md#storagesettings) |  |
| `volumesExist` | boolean | Whether volumes exist, whose history is in this store: it can then be changed, but not for another bucket or directory. |

## GetStorageUsage

`POST /spin.v1.AdminService/GetStorageUsage`

What the store holds, by what each object is for: every volume's objects and the layers its commits name, with the workspace, image or extension release it is. As the storage-gc job last read it - daily, or when an admin runs it - never the bucket itself.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `volumes` | list of [StoredVolume](https://spin-stack.github.io/website/reference/api/types.md#storedvolume) | Heaviest first. |
| `unreferencedLayers` | [StorageTally](https://spin-stack.github.io/website/reference/api/types.md#storagetally) | Layers no commit in the store names: the storage GC's to collect. |
| `other` | [StorageTally](https://spin-stack.github.io/website/reference/api/types.md#storagetally) | Everything outside volumes/ and layers/: the control plane's election records. |
| `measuredAt` | `Timestamp` |  |

## GetTelemetryStatus

`POST /spin.v1.AdminService/GetTelemetryStatus`

What the collector has sent and failed to send over its last minute, as the watch on its machine read it from the collector itself.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `reportedAt` | `Timestamp` | Unset: the watch has not reported since this control plane started - no collector, or not yet. |
| `windowSeconds` | integer (int32) | How long the counts below are over. |
| `spans` | [TelemetryFlow](https://spin-stack.github.io/website/reference/api/types.md#telemetryflow) | Into the installation's store, after the traces policy chose which to keep. |
| `samples` | [TelemetryFlow](https://spin-stack.github.io/website/reference/api/types.md#telemetryflow) | Into the installation's store of metrics. |

## GetVSCodeServers

`POST /spin.v1.AdminService/GetVSCodeServers`

The VS Code servers workspaces are given (the vscode-server extension): each release, and the publish this control plane ran since it started, if it ran one.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `releases` | list of [ExtensionRelease](https://spin-stack.github.io/website/reference/api/types.md#extensionrelease) | Newest first. |
| `publish` | [VSCodeServerPublish](https://spin-stack.github.io/website/reference/api/types.md#vscodeserverpublish) |  |
| `unavailable` | string | Why this control plane cannot publish - no object store, or a build with no machine tools to make the image with - and empty when it can. |

## ListAllowedEmails

`POST /spin.v1.AdminService/ListAllowedEmails`

List allowed emails/domains

**Request**

| Field | Type | What it is |
|---|---|---|
| `page` | [PageRequest](https://spin-stack.github.io/website/reference/api/types.md#pagerequest) |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `entries` | list of [AllowedEmail](https://spin-stack.github.io/website/reference/api/types.md#allowedemail) |  |
| `page` | [PageResponse](https://spin-stack.github.io/website/reference/api/types.md#pageresponse) |  |

## ListAuditLog

`POST /spin.v1.AdminService/ListAuditLog`

Read the audit log, newest first. There is no RPC that writes one: an entry is written by the operation it records, so a log an API could add to would be one an API could forge.

**Request**

| Field | Type | What it is |
|---|---|---|
| `page` | [PageRequest](https://spin-stack.github.io/website/reference/api/types.md#pagerequest) |  |
| `actorId` | string | Each filter is optional and they are combined with AND; an empty request is the whole log, newest first. |
| `action` | string |  |
| `resourceType` | string |  |
| `since` | `Timestamp` | since bounds the listing at the older end; an audit log is read forward from an incident, and the whole of one is not what a page wants. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `entries` | list of [AuditEntry](https://spin-stack.github.io/website/reference/api/types.md#auditentry) |  |
| `page` | [PageResponse](https://spin-stack.github.io/website/reference/api/types.md#pageresponse) |  |
| `actions` | list of string | actions and resource_types are every value this installation can record, so a reader building a filter does not have to know them. They come from the code rather than from a DISTINCT over the log: the log is the one table that only grows, and a vocabulary that is empty until something happens is a filter that is empty when it matters. |
| `resourceTypes` | list of string |  |
| `totalCapped` | boolean | total_capped says page.total_count is where counting stopped, not the count: the log is counted up to a bound, since counting all of it is reading all of it on every page. |

## ListConfigChanges

`POST /spin.v1.AdminService/ListConfigChanges`

What the config file says now and the installation does not, for an administrator to decide: the file seeds an installation once, and after that its changes are proposals.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `changes` | list of [ConfigChange](https://spin-stack.github.io/website/reference/api/types.md#configchange) |  |

## ListJobRuns

`POST /spin.v1.AdminService/ListJobRuns`

One job's runs, newest first.

**Request**

| Field | Type | What it is |
|---|---|---|
| `job` | string |  |
| `limit` | integer (int32) | At most this many, newest first; zero is the default of 50. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `runs` | list of [JobRun](https://spin-stack.github.io/website/reference/api/types.md#jobrun) |  |

## ListJobs

`POST /spin.v1.AdminService/ListJobs`

The control plane's periodic work - backups, cleanups, the object store's garbage collection - with how often each runs and how its last run went.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `jobs` | list of [Job](https://spin-stack.github.io/website/reference/api/types.md#job) |  |

## ListRekeys

`POST /spin.v1.AdminService/ListRekeys`

The disks being re-keyed, and the ones a revoked host's keys marked for it.

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `rekeys` | list of [VolumeRekey](https://spin-stack.github.io/website/reference/api/types.md#volumerekey) |  |

## ListResourcePresets

`POST /spin.v1.AdminService/ListResourcePresets`

List resource presets

**Request**

| Field | Type | What it is |
|---|---|---|
| `includeDisabled` | boolean | Include disabled presets (admin only) |
| `page` | [PageRequest](https://spin-stack.github.io/website/reference/api/types.md#pagerequest) |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `presets` | list of [ResourcePreset](https://spin-stack.github.io/website/reference/api/types.md#resourcepreset) |  |
| `page` | [PageResponse](https://spin-stack.github.io/website/reference/api/types.md#pageresponse) |  |

## ListUserAPIKeys

`POST /spin.v1.AdminService/ListUserAPIKeys`

List another user's API keys. The secret is never stored, so this is what an admin can know about a key: what it is called, when it was made, when it was last used.

**Request**

| Field | Type | What it is |
|---|---|---|
| `userId` | string |  |
| `page` | [PageRequest](https://spin-stack.github.io/website/reference/api/types.md#pagerequest) |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `apiKeys` | list of [APIKey](https://spin-stack.github.io/website/reference/api/types.md#apikey) |  |
| `page` | [PageResponse](https://spin-stack.github.io/website/reference/api/types.md#pageresponse) |  |

## ListUsers

`POST /spin.v1.AdminService/ListUsers`

List all users

**Request**

| Field | Type | What it is |
|---|---|---|
| `page` | [PageRequest](https://spin-stack.github.io/website/reference/api/types.md#pagerequest) |  |
| `query` | string | Optional: filter by name or email |
| `adminsOnly` | boolean | Optional: only return admin users |

**Response**

| Field | Type | What it is |
|---|---|---|
| `users` | list of [User](https://spin-stack.github.io/website/reference/api/types.md#user) |  |
| `page` | [PageResponse](https://spin-stack.github.io/website/reference/api/types.md#pageresponse) |  |

## PublishVSCodeServers

`POST /spin.v1.AdminService/PublishVSCodeServers`

Publish the servers of the newest stable VS Code releases, or of the commits named, as a new release. Answers once the publish has begun - it downloads a gigabyte - and GetVSCodeServers says how it goes.

**Request**

| Field | Type | What it is |
|---|---|---|
| `newest` | integer (int32) | The newest stable releases to carry: 2 is the current one and the one before it. |
| `commits` | list of string | Commits to carry besides, each the second line of `code --version`. |

**Response**

No fields: `{}`.

## RekeyVolume

`POST /spin.v1.AdminService/RekeyVolume`

Re-key a workspace's disk: copy it whole under a key of its own, point the workspace at the copy, and delete the disk - which shreds the key assumed taken and every snapshot of the disk, since those are what that key opens. The workspace must be stopped or quarantined. Refused for a disk something descends from. Audited.

**Request**

| Field | Type | What it is |
|---|---|---|
| `workspaceId` | string |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `targetVolumeId` | string |  |

## RemoveAllowedEmail

`POST /spin.v1.AdminService/RemoveAllowedEmail`

Remove allowed email/domain

**Request**

| Field | Type | What it is |
|---|---|---|
| `id` | string |  |

**Response**

No fields: `{}`.

## RemoveStoredVolume

`POST /spin.v1.AdminService/RemoveStoredVolume`

Remove an image, an extension release, an unclaimed volume or an orphan from the store: its row, then its volume, whose key is shredded; storage-gc collects its layers on its next run. Refused for one that is kept (StoredVolume.kept_because), one placed on a host, and a workspace's disk. Audited.

**Request**

| Field | Type | What it is |
|---|---|---|
| `volumeId` | string |  |

**Response**

No fields: `{}`.

## RetryPlatformImages

`POST /spin.v1.AdminService/RetryPlatformImages`

Look again now rather than when the next retry is due.

**Request**

No fields: `{}`.

**Response**

No fields: `{}`.

## RevokeUserAPIKey

`POST /spin.v1.AdminService/RevokeUserAPIKey`

Revoke another user's API key - the offboarding an admin cannot do by asking the user, who may already be gone. It is audited with the admin who did it.

**Request**

| Field | Type | What it is |
|---|---|---|
| `userId` | string |  |
| `id` | string |  |

**Response**

No fields: `{}`.

## RunJobNow

`POST /spin.v1.AdminService/RunJobNow`

Run a job now rather than when it is next due. Refused while it is running: a second run alongside the first would do the same work twice. Audited with the admin who asked.

**Request**

| Field | Type | What it is |
|---|---|---|
| `job` | string |  |

**Response**

No fields: `{}`.

## SetRegistrationMode

`POST /spin.v1.AdminService/SetRegistrationMode`

Set registration mode

**Request**

| Field | Type | What it is |
|---|---|---|
| `mode` | [RegistrationMode](https://spin-stack.github.io/website/reference/api/types.md#registrationmode) |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `mode` | [RegistrationMode](https://spin-stack.github.io/website/reference/api/types.md#registrationmode) |  |

## UntagImage

`POST /spin.v1.AdminService/UntagImage`

Take a ref off what it names: no new workspace gets those bytes by it, the disks cloned from them go on reading them, and a later publish under the ref tags the new bytes. Audited.

**Request**

| Field | Type | What it is |
|---|---|---|
| `ref` | string |  |

**Response**

No fields: `{}`.

## UpdateInstallationSettings

`POST /spin.v1.AdminService/UpdateInstallationSettings`

Update installation settings

**Request**

| Field | Type | What it is |
|---|---|---|
| `settings` | [InstallationSettings](https://spin-stack.github.io/website/reference/api/types.md#installationsettings) |  |
| `updateMask` | `FieldMask` | Required: only the named fields of settings are written, so a form that edits one setting cannot reset the others to their zero values. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `settings` | [InstallationSettings](https://spin-stack.github.io/website/reference/api/types.md#installationsettings) |  |

## UpdateNetworkPolicy

`POST /spin.v1.AdminService/UpdateNetworkPolicy`

**Request**

| Field | Type | What it is |
|---|---|---|
| `policy` | [NetworkPolicyRule](https://spin-stack.github.io/website/reference/api/types.md#networkpolicyrule) |  |

**Response**

No fields: `{}`.

## UpdateResourcePreset

`POST /spin.v1.AdminService/UpdateResourcePreset`

Update a resource preset

**Request**

| Field | Type | What it is |
|---|---|---|
| `id` | string | Required: preset ID to update |
| `name` | string | Optional: new display name |
| `description` | string | Optional: new description |
| `cpuCores` | integer (int32) | Optional: new CPU cores |
| `memoryMb` | integer (int32) | Optional: new memory |
| `diskGb` | integer (int32) | Optional: new disk |
| `burst` | [BurstMode](https://spin-stack.github.io/website/reference/api/types.md#burstmode) | Optional: new burst mode |
| `isDefault` | boolean | Optional: make this the default |
| `enabled` | boolean | Optional: enable/disable |
| `sortOrder` | integer (int32) | Optional: new display order |
| `burstFloorCpuCores` | integer (int32) | Optional: new burst floor |
| `burstFloorMemoryMb` | integer (int32) | Optional: new burst floor |
| `bandwidthMbps` | integer or null (int32) | Optional: a new bandwidth. Present, since zero - unshaped - is an answer and not "keep". |
| `rpoSeconds` | integer or null (int32) | Optional: a new RPO. Present, since zero - the installation's - is an answer and not "keep". |

**Response**

| Field | Type | What it is |
|---|---|---|
| `preset` | [ResourcePreset](https://spin-stack.github.io/website/reference/api/types.md#resourcepreset) |  |

## UpdateSecuritySettings

`POST /spin.v1.AdminService/UpdateSecuritySettings`

Update security settings

**Request**

| Field | Type | What it is |
|---|---|---|
| `settings` | [SecuritySettings](https://spin-stack.github.io/website/reference/api/types.md#securitysettings) |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `settings` | [SecuritySettings](https://spin-stack.github.io/website/reference/api/types.md#securitysettings) |  |

## UpdateStorageSettings

`POST /spin.v1.AdminService/UpdateStorageSettings`

Set where the object store is, checked against it before it is written; every control plane restarts within seconds to open it. Another store while volumes exist is refused.

**Request**

| Field | Type | What it is |
|---|---|---|
| `bucket` | string |  |
| `endpoint` | string |  |
| `region` | string |  |
| `accessKeyId` | string |  |
| `secretAccessKey` | string or null | Absent keeps the secret there is. |
| `dir` | string |  |
| `principal` | string |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `settings` | [StorageSettings](https://spin-stack.github.io/website/reference/api/types.md#storagesettings) |  |

## UpdateUser

`POST /spin.v1.AdminService/UpdateUser`

Update user (can toggle is_instance_admin, disable user)

**Request**

| Field | Type | What it is |
|---|---|---|
| `id` | string | Required: user ID |
| `isInstanceAdmin` | boolean or null | Toggle admin status |
| `disabled` | boolean or null | Disable/enable user |
| `disabledReason` | string or null | Reason for disabling (shown to user) |

**Response**

| Field | Type | What it is |
|---|---|---|
| `user` | [User](https://spin-stack.github.io/website/reference/api/types.md#user) |  |
