> ## 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.

# API reference

> Base URLs, CLI usage, identifiers, organization parameters, field casing, pagination, and retrying writes for the Business and Platform APIs.

Andromeda exposes two HTTP APIs. Both take and return JSON, and both authenticate you with the same bearer token.

| API | Base URL | What it holds |
| - | - | - |
| Business | `https://business-api.andromedacluster.xyz` | You, your organizations, permissions, SSH keys, and the commercial record of each cluster |
| Platform | `https://platform-v2.andromedacluster.xyz` | The running cluster: its state, machines, SSH endpoint, kubeconfig, and Slurm configuration |

Each API serves its OpenAPI 3.1 document at `/openapi.json` and a browsable version at `/docs`. The pages here cover
the subset of endpoints a cluster user needs. Everything else in those documents is either internal or not yet
documented for customers.

## Endpoints

| Page | Endpoints |
| - | - |
| [Identity](/api-reference/business/identity) | `GET /me`, `GET /organizations/{orgID}`, `GET /organizations/{orgID}/members` |
| [Permissions](/api-reference/business/permissions) | `GET /permission-profiles`, `GET` and `PUT /organizations/{orgID}/members/{userID}/permissions`, `GET /me/resource-grants`, `GET /resource-access` |
| [SSH keys](/api-reference/business/ssh-keys) | `GET` and `POST /me/ssh-keys`, `POST /me/ssh-keys/{keyID}/revoke`, `PUT /me/login-preferences`; deprecated alias `POST /organizations/{orgID}/ssh-keys` |
| [Clusters (Business)](/api-reference/business/clusters) | `GET /organizations/{orgID}/clusters`, `…/clusters/{clusterID}`, `…/clusters/{clusterID}/access` |
| [Clusters (Platform)](/api-reference/platform/clusters) | `GET /clusters`, `/clusters/{clusterId}`, `/clusters/{clusterId}/options`, `/clusters/{clusterId}/machines` |
| [Cluster access](/api-reference/platform/access) | `GET /clusters/{clusterId}/ssh-access`, `GET /clusters/{clusterId}/kubeconfig` |
| [Slurm configuration](/api-reference/platform/scheduler) | `GET` and `PUT /clusters/{clusterId}/scheduler-configuration`, `GET` and `PUT /organizations/{orgId}/scheduler-configuration` |
| [Storage](/api-reference/platform/storage) | `GET /organizations/{orgId}/storage` |
| [Errors](/api-reference/errors) | Error bodies and codes for both APIs |

## Making requests

The examples use the `andromeda` CLI, which signs you in and attaches your token to each request. See
[Authentication](/clusters/authentication) to set it up. `andromeda call` takes the API name, the HTTP method,
and the path:

```sh theme={null}
andromeda call business GET /me
andromeda call platform GET "/clusters?businessOrgId=$ORG_ID"
```

Flags go before the API name. The CLI stops reading flags at the first positional argument, so a flag placed after
the path is an error:

```sh theme={null}
andromeda call \
  --header "Idempotency-Key: $(uuidgen)" \
  --header 'Audit-Reason: Register laptop key' \
  --data @key.json \
  business POST /me/ssh-keys
```

| Flag | Meaning |
| - | - |
| `-H`, `--header 'Name: value'` | Add a request header. Repeat for more. `Authorization` is refused, since the CLI manages it. |
| `-d`, `--data value` | Send `value` as the request body. `@path` reads a file, `@-` reads standard input. Sets `Content-Type: application/json` unless you pass one. |

On a `2xx` response the CLI writes the response body to standard output and exits `0`. It does not print the status
or headers. On any other status it writes one line to standard error and exits `1`:

```text theme={null}
platform returned 403 Forbidden: {"code":"forbidden","message":"operation is not permitted","retryable":false}
```

The CLI does not follow redirects. A `3xx` is reported the same way as an error.

To call the APIs from your own HTTP client, send `Authorization: Bearer <access token>`. The
[authentication guide](/clusters/authentication#call-the-api-from-your-own-client) explains which tokens are
accepted.

## Identifiers

| Object | Format | Example |
| - | - | - |
| Organization | `org_` + ULID | `org_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3` |
| User | `user_` + ULID | `user_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4` |
| SSH key | `ssh_key_` + ULID | `ssh_key_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5` |
| Cluster | UUID | `0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b` |
| Machine | UUID | `0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a60` |

A cluster has one ID, and both APIs use it. You also type it as part of your SSH login name.

## Organization in the request

Every read is scoped to one organization. Business puts the organization in the path. Platform takes it as a query
parameter for cluster routes and in the path for organization routes. The query parameter has two names:

| Platform endpoints | Organization parameter |
| - | - |
| `/clusters`, `/clusters/{clusterId}`, `/options`, `/machines`, `/scheduler-configuration` | `businessOrgId` |
| `/clusters/{clusterId}/ssh-access`, `/clusters/{clusterId}/kubeconfig` | `organizationId` |
| `/organizations/{orgId}/storage`, `/organizations/{orgId}/scheduler-configuration` | path, `{orgId}` |

The parameter is required when you call as a person, even where the OpenAPI document marks it optional. Without it,
Platform returns `400` with code `invalid_organization`, or `422` where the document marks it required.

## Field names

Business uses `snake_case` throughout. Platform uses `camelCase`, except for `/ssh-access` and `/machines`, which
use `snake_case`. Each reference page shows the exact field names.

## Pagination

Platform lists are ordered by ID and take `limit` (1 to 200, default 50) and `after` (an ID). When more items exist,
the response carries a token. Pass it as `after` to get the next page. The token is `nextPageToken` on `/clusters` and
`next_page_token` on `/machines`. It is absent on the last page.

```sh theme={null}
andromeda call platform GET "/clusters?businessOrgId=$ORG_ID&limit=50&after=$NEXT"
```

The Business lists on these pages return every item in one response.

## Retrying writes

Business write endpoints take two headers:

| Header | Value |
| - | - |
| `Idempotency-Key` | 1 to 200 characters that you choose, one per logical operation. A UUID works. |
| `Audit-Reason` | 1 to 500 characters saying why you made the change. Stored with the change. |

If a request fails in transit, send it again with the same key and body. A replay returns `200` with the same result
as the first success, which is `201` when the request created something. Reusing a key with a different body returns
`409` with code `idempotency_mismatch`. Your permissions are checked again on every replay.

The Platform `PUT` endpoints on these pages replace a whole document. Sending the same document twice leaves the
same result, so they take no idempotency header.

## Versioning

Paths carry no version. Each API's OpenAPI document states its current version in `info.version`.


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