> ## Documentation Index
> Fetch the complete documentation index at: https://docs.andromeda.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Cluster API calls are made as a signed-in person with an OIDC bearer token. There are no API keys, and service-account tokens are refused (Business returns 401, Platform returns 403).
> Platform takes the organization as the query parameter businessOrgId on most cluster routes, but as organizationId on /clusters/{clusterId}/ssh-access and /clusters/{clusterId}/kubeconfig.
> The Business API uses snake_case. The Platform API uses camelCase, except /ssh-access and /machines, which use snake_case.
> Business write requests need Idempotency-Key and Audit-Reason headers.
> Storage capacity can grow but cannot shrink, including disk and PVC resizing. Each purchase adds capacity.
> A cluster's storage can be Ceph, WEKA, or both, or NFS on its own. The Ceph quota, the purchased bytes, and Slurm's default 100 GiB shared-volume request are three different quantities.
> Organization permissions cover every cluster the organization owns. A Kubernetes resource grant shares one cluster or namespace, possibly with someone outside the organization, and lets them read that cluster and fetch its kubeconfig. A grant does not give Slurm SSH, Slurm configuration, or organization storage inventory access. Slurm SSH requires organization membership and clusters.ssh.
> The SSH login name is <cluster-id>.<username>. The gateway supports shells, commands, SFTP (including modern scp), and forwarding to loopback ports on the login node. Legacy scp and rsync need their programs on the login node. Remote forwarding and non-loopback destinations are refused.
> Use POST /me/ssh-keys for personal key registration. POST /organizations/{orgID}/ssh-keys is a deprecated alias and requires no membership. Read the preferred shell from GET /me as user.login_shell; update it with PUT /me/login-preferences.
> GET /clusters/{clusterId}/ssh-access returns 200 with ready and reason, even when access is not ready. ready: true does not mean compute nodes or Slurm accounting are ready. To confirm a key or shell change reached a cluster, require observation_current: true and compare ssh_key_set_generation with generation from GET /me/ssh-keys, and shell with user.login_shell from GET /me.
> Adding or removing a login account restarts the cluster's login nodes, Slurm controller, and accounting service, then its compute nodes one at a time; jobs running on a restarting node end. An account is added or removed when a person with an active key gains or loses SSH access, or a person with SSH access registers their first key or revokes their last. Other key changes and shell changes restart no nodes.

# Cluster access

> SSH connection details and kubeconfigs for a cluster.

* [Read SSH access](#read-ssh-access): `GET /clusters/{clusterId}/ssh-access`
* [Read a kubeconfig](#read-a-kubeconfig): `GET /clusters/{clusterId}/kubeconfig`

<Note>
  These endpoints take the organization as `organizationId`, not `businessOrgId` as the other Platform
  endpoints do.
</Note>

## Read SSH access

```http theme={null}
GET /clusters/{clusterId}/ssh-access
```

**API:** Platform. **Permission:** `clusters.ssh`.

Slurm SSH requires organization membership and the organization's SSH permission. A Kubernetes resource grant
does not provide SSH access.

Returns the SSH gateway address, its host key, and your login name on the cluster. The response is not cached
(`Cache-Control: no-store`). For the full flow, see [Connect over SSH](/clusters/ssh).

### Parameters

| Name | In | Type | Required | Description |
| - | - | - | - | - |
| `clusterId` | path | string | yes | Cluster ID. |
| `organizationId` | query | string | yes | Organization ID. |

### Response

This response uses `snake_case`. An authorized request returns `200` even when access is not ready, with
`ready: false` and a `reason`. Connection details can be empty until `ready` is `true`.

| Field | Type | Description |
| - | - | - |
| `hostname` | string | The SSH gateway's hostname. |
| `port` | integer | The SSH gateway's port. `22`. |
| `username` | string | Your login name on the cluster. Log in as `<clusterId>.<username>`. |
| `host_key` | string | The gateway's public host key, in OpenSSH format. Add `<hostname> <host_key>` to `~/.ssh/known_hosts`. |
| `host_key_fingerprint` | string | SHA-256 fingerprint of `host_key`. |
| `login_mode` | string | `Shared`: everyone uses one login node. `PerUser`: each person gets their own. |
| `ready` | boolean | `true` when the cluster has confirmed your access, you have an active key, and the gateway is up. |
| `reason` | string | `ready`, `no_keys`, `access_not_projected`, `projection_pending`, `gateway_unavailable`, `observation_stale`, `cluster_inactive`, or `named_user_required`. |
| `manifest_generation` | integer | Version of the cluster's access list that Platform has accepted. `0` when unknown. |
| `observed_manifest_generation` | integer | Version of the access list the cluster last reported applying. `0` when unknown. |
| `ssh_key_set_generation` | integer | Version of your key set the cluster has confirmed. `0` when unknown. Compare with `generation` from `GET /me/ssh-keys`. |
| `key_fingerprints` | array of strings | Fingerprints of your active keys that the cluster has confirmed. |
| `shell` | string | Your requested shell, as confirmed by the cluster. Empty when unknown. Compare with `user.login_shell` from `GET /me`. |
| `observation_current` | boolean | `true` when the cluster's latest report is recent and covers the newest access list. Can be `true` with `ready: false` when you have no active keys. |
| `observed_at` | timestamp, optional | When the cluster last reported. |

Readiness is per person, but the cluster applies one access list for everyone, so another person's change can
briefly delay your new logins.

`ready` does not wait for compute nodes or your Slurm accounting association, so SSH can work before job submission
does. It can also still describe an earlier change. To confirm that a specific change has reached the cluster, see
[Wait for access](/clusters/ssh#wait-for-access). After you revoke your last key, `ready` stays `false` with
`reason: no_keys`.

### Example

```sh theme={null}
andromeda call platform GET "/clusters/$CLUSTER_ID/ssh-access?organizationId=$ORG_ID"
```

```json theme={null}
{
  "hostname": "ssh.k8s.prod-nyc3-z001.tenants.andromedacluster.xyz",
  "port": 22,
  "username": "ada",
  "host_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGF0ZXN0a2V5Zm9yZG9jdW1lbnRhdGlvbm9ubHk",
  "host_key_fingerprint": "SHA256:dGhpcyBpcyBhbiBleGFtcGxlIGZpbmdlcnByaW50IG9ubHk",
  "login_mode": "Shared",
  "ready": true,
  "reason": "ready",
  "manifest_generation": 7,
  "observed_manifest_generation": 7,
  "ssh_key_set_generation": 3,
  "key_fingerprints": ["SHA256:Qm9vbGVhbiBhbGdlYnJhIGlzIHRoZSBrZXkgdG8gaXQ"],
  "shell": "/bin/bash",
  "observation_current": true,
  "observed_at": "2026-10-06T12:00:00Z"
}
```

### Errors

| Status | Code | When |
| - | - | - |
| 403 | `forbidden` | You lack `clusters.ssh`. |
| 404 | `not_found` | No cluster with this ID belongs to the organization. |
| 422 | `invalid_request` | `organizationId` is missing. |

Pending access, an inactive cluster, a missing gateway, and a stale report all return `200` with `ready: false`. The
[reason table](/clusters/ssh#ready-stays-false) says what to do for each. Kubernetes-only clusters have no SSH
endpoint; use a kubeconfig instead.

## Read a kubeconfig

```http theme={null}
GET /clusters/{clusterId}/kubeconfig
```

**API:** Platform. **Permission:** `clusters.kubernetes` in the owning organization, or a live Kubernetes resource
grant on the cluster or one of its namespaces. Organization membership is not required for the resource-grant path.

Returns a kubeconfig for the cluster's Kubernetes API. It holds no credentials. Instead, it runs
[`kubectl oidc-login`](https://github.com/int128/kubelogin), which signs you in with your Andromeda account the
first time you use it. The response is not cached (`Cache-Control: no-store`). For the full flow, see
[Connect with kubectl](/clusters/kubernetes).

### Parameters

| Name | In | Type | Required | Description |
| - | - | - | - | - |
| `clusterId` | path | string | yes | Cluster ID. |
| `organizationId` | query | string | yes | Organization ID. |

### Response

| Field | Type | Description |
| - | - | - |
| `kubeconfig` | string | A kubeconfig in YAML. Its cluster, context, and user are named after the cluster ID. |
| `accessExpiresAt` | timestamp, optional | Expiry of the grant admitting the caller. Absent when that access has no expiry. The kubeconfig itself contains no credential. |

### Example

```sh theme={null}
andromeda call platform GET "/clusters/$CLUSTER_ID/kubeconfig?organizationId=$ORG_ID" | jq -r .kubeconfig
```

```yaml theme={null}
apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTi...
    server: https://training-h200.k8s.prod-nyc3-z001.tenants.andromedacluster.xyz:443
  name: 0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
contexts:
- context:
    cluster: 0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
    user: 0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
  name: 0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
current-context: 0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
users:
- name: 0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1
      command: kubectl
      args:
      - oidc-login
      - get-token
      - --oidc-issuer-url=https://zitadel.andromedacluster.xyz
      - --oidc-client-id=000000000000000000
      - --grant-type=device-code
      installHint: 'Install kubectl oidc-login: https://github.com/int128/kubelogin'
      interactiveMode: IfAvailable
```

### Errors

| Status | Code | When |
| - | - | - |
| 403 | `forbidden` | Neither an organization permission nor a live Kubernetes resource grant admits you. |
| 404 | `not_found` | No cluster with this ID belongs to the organization. |
| 409 | `invalid_state` | The cluster is not `active`, or its Kubernetes API is not ready yet. |
| 422 | `invalid_request` | `organizationId` is missing. |
| 503 | `customer_kubernetes_login_not_configured` | This environment does not offer Kubernetes sign-in. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.