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

# Organizations

> Group members, share credits, and scope API keys to an org

An organization (org) is a shared workspace. Members of an org share its credit balance and its API keys, and every billable resource, runs, VMs, deployments, is tagged with the org that ran it.

Every Lyceum account has one org created automatically at signup; you can stay on that single org or create additional ones (for a team, a client project, a separate billing pool).

## Concepts

* **Default org**, Every user has exactly one membership flagged as their default. Requests that don't pin an org fall back to it. The default is created at signup and you must always have one.

* **Active org**, The org the dashboard (or CLI) is currently acting on. The org switcher in the sidebar changes the active org; under the hood the dashboard sends `X-Org-Slug: <slug>` on every request.

* **Roles**, Each member of an org has one of three roles:

  | Role     | Can do                                                                     |
  | -------- | -------------------------------------------------------------------------- |
  | `owner`  | Everything: change roles, edit org name/slug/billing email, delete the org |
  | `admin`  | Manage members, invites, and API keys                                      |
  | `member` | Read org details, see balance, use the org's API keys and credits          |

* **Slug**, A short URL-safe identifier (`a-z`, `0-9`, `-`). Slugs are unique platform-wide and stable URLs in the dashboard (`/orgs/{slug}`). They can be renamed by an owner.

## Using the dashboard

The org switcher lives in the sidebar. Click it to:

* See every org you belong to and your role in each
* Switch the active org, every page in the dashboard refetches against the new org
* Open **Organizations** to create a new org, see pending invites you've received, or jump into an org's settings

The **Organization** settings page (`/orgs/{slug}`) shows the members table, pending invites, and the org's current balance. Owners get extra controls for renaming the org, changing the billing email, and deleting it.

## Inviting members

Add someone by email from the org settings page (or `POST /orgs/{slug}/members`). The response shape depends on whether the invitee already has a Lyceum account:

* **Existing user**, They're added directly. They receive a "you've been added to *Org Name*" email and the invite shows `status: "added"`.
* **New user**, A pending invite is created and an invite email is sent. The response is `status: "invited"`. The link in that email goes to `https://dashboard.lyceum.technology/invite/{token}`. When the user signs up or signs in with the same email, the invite is auto-accepted by the auth hook.

Pending invites can be revoked at any time (`DELETE /orgs/{slug}/invites/{invite_id}`). Once a user accepts, the row is kept for audit and a new pending invite can be issued.

<Note>
  Only one pending invite per (org, email) can exist at a time. Revoke the old one first if you need to re-send.
</Note>

## Org context on API requests

Every request that touches a billable resource (runs, VMs, deployments, balance, history) is resolved against an org using this order:

1. **API key with an embedded `org_id`**, API keys created under `/orgs/{slug}/api-keys` are pinned to that org. Authentication and org context come from the key in a single step.
2. **JWT + `X-Org-Slug` header**, If you authenticated with a JWT, pass the slug of the org you want to act on as `X-Org-Slug: <slug>`. The server verifies that you're a member of that org.
3. **Fallback**, If neither is provided, the request falls back to your default org.

```bash theme={null}
# Explicit org via header
curl https://api.lyceum.technology/api/v2/external/billing/credits \
  -H "Authorization: Bearer eyJhbGciOi..." \
  -H "X-Org-Slug: acme"

# Org pinned by the API key (no header needed)
curl https://api.lyceum.technology/api/v2/external/billing/credits \
  -H "Authorization: Bearer lk_..."
```

## CLI

Org management commands live under `lyceum org`. The CLI tracks the active org in `~/.lyceum/config.json`; once set, it sends `X-Org-Slug` on every subsequent request.

```bash theme={null}
# Switch active org
lyceum org list
lyceum org use acme         # Pin acme as active
lyceum org current          # Show what's currently active
lyceum org unset            # Fall back to your default org

# Create / inspect / update / delete
lyceum org create "Acme Corp" --slug acme --billing-email billing@acme.com
lyceum org show              # Defaults to active org
lyceum org update --name "Acme Inc"
lyceum org delete acme

# Balance and ledger
lyceum org balance
lyceum org transactions list --kind debit --limit 50
lyceum org transactions aggregate --category vms --group-by hardware_profile

# Members
lyceum org members list
lyceum org members add user@example.com --role member
lyceum org members role <user_id> --role admin
lyceum org members remove <user_id>

# Invites
lyceum org invites list                   # Pending invites for this org
lyceum org invites revoke <invite_id>
lyceum org invites mine                   # Invites sent to your email
lyceum org invites accept <invite_id>

# Org-scoped API keys
lyceum org keys list
lyceum org keys create "ci-pipeline" --expires-at 2027-01-01T00:00:00Z
lyceum org keys revoke <key_id>
```

You can also override the active org for one command without changing the pin:

```bash theme={null}
lyceum --org other-team workloads list   # or set LYCEUM_ORG=other-team
```

## Billing

Credits are tracked per org. The org's billing balance, transaction ledger, and category aggregates are available at:

| Method | Endpoint                              | Description                                                                            |
| ------ | ------------------------------------- | -------------------------------------------------------------------------------------- |
| `GET`  | `/orgs/{slug}/balance`                | Current credit balance and suspension status                                           |
| `GET`  | `/orgs/{slug}/transactions`           | Newest-first ledger; filters: `kind`, `source`, cursor (`before`+`before_id`), `limit` |
| `GET`  | `/orgs/{slug}/transactions/aggregate` | Group debits by a metadata key for a category                                          |

Aggregate categories and the metadata keys you can group by:

| `category`                               | What it covers                         | Valid `group_by`                   |
| ---------------------------------------- | -------------------------------------- | ---------------------------------- |
| `vms`                                    | VM running time                        | `vm_id`, `hardware_profile`        |
| `executions`                             | Serverless run execution               | `execution_id`, `hardware_profile` |
| `inference_prompt_tokens`                | Dedicated inference prompt tokens      | `deployment_id`                    |
| `inference_completion_tokens`            | Dedicated inference completion tokens  | `deployment_id`                    |
| `serverless_inference_prompt_tokens`     | Serverless inference prompt tokens     | `deployment_id`                    |
| `serverless_inference_completion_tokens` | Serverless inference completion tokens | `deployment_id`                    |

Stripe top-ups credit the org that was active when the checkout session was created, the org id is captured in the Stripe session metadata so the webhook routes credits back to the right org even if the user switches active org before the payment completes.

See [Billing](/docs/account/billing) for top-ups, history, and invoices.

## REST API

### Orgs

| Method   | Endpoint       | Role required                        |
| -------- | -------------- | ------------------------------------ |
| `GET`    | `/orgs`        | Authenticated                        |
| `POST`   | `/orgs`        | Authenticated (caller becomes owner) |
| `GET`    | `/orgs/{slug}` | owner / admin / member               |
| `PATCH`  | `/orgs/{slug}` | owner                                |
| `DELETE` | `/orgs/{slug}` | owner                                |

`DELETE /orgs/{slug}` returns `409` if any member has the org as their default, or if the org still has live VMs, runs, or deployments.

### Members

| Method   | Endpoint                         | Role required |
| -------- | -------------------------------- | ------------- |
| `GET`    | `/orgs/{slug}/members`           | owner / admin |
| `POST`   | `/orgs/{slug}/members`           | owner / admin |
| `PATCH`  | `/orgs/{slug}/members/{user_id}` | owner         |
| `DELETE` | `/orgs/{slug}/members/{user_id}` | owner / admin |

`DELETE` is rejected with `400` if the target is the last owner, and `409` if the org is the target's default org.

### Invites

| Method   | Endpoint                           | Auth                             |
| -------- | ---------------------------------- | -------------------------------- |
| `GET`    | `/orgs/{slug}/invites`             | owner / admin                    |
| `DELETE` | `/orgs/{slug}/invites/{invite_id}` | owner / admin                    |
| `GET`    | `/invites/mine`                    | Authenticated                    |
| `POST`   | `/invites/mine/{invite_id}/accept` | Authenticated (email must match) |
| `GET`    | `/invites/{token}`                 | Public (token is the capability) |
| `POST`   | `/invites/{token}/accept`          | Authenticated (email must match) |

### Org-scoped API keys

| Method   | Endpoint                                    | Role required |
| -------- | ------------------------------------------- | ------------- |
| `GET`    | `/orgs/{slug}/api-keys`                     | owner / admin |
| `POST`   | `/orgs/{slug}/api-keys`                     | owner / admin |
| `POST`   | `/orgs/{slug}/api-keys/{key_id}/regenerate` | owner / admin |
| `DELETE` | `/orgs/{slug}/api-keys/{key_id}`            | owner / admin |

The plaintext key is returned exactly once at creation or regeneration, store it immediately. See [API Keys](/docs/configuration/api-keys) for details.
