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

# Organizations

> The billing entity that owns members, keys, and resources.

An **organization** is the top-level container and billing entity. It holds the [credit balance](/docs/api/credits), owns every agent, computer, skill, and file, and has **members** (users with a role) and **API keys** (programmatic access). Every request is scoped to exactly one organization via its [API key](/docs/api/authentication).

## The organization object

<ResponseField name="id" type="string">Unique id (e.g. `org_01H...`).</ResponseField>
<ResponseField name="name" type="string">Display name.</ResponseField>
<ResponseField name="balance_micro_usd" type="integer">Current [credit balance](/docs/api/credits), in micro-USD.</ResponseField>
<ResponseField name="created_at" type="string">Creation timestamp.</ResponseField>

## Create an organization

`POST /v1/organizations` → `201 Created`

The only write that is **not** scoped to an organization — it is how an existing caller gets *another* one. Authenticate with either an `admin` [API key](/docs/api/authentication) or a signed-in user session; an unauthenticated call is refused with `401`. Your first organization comes from signing up, not from this route.

<ParamField body="name" type="string" required>Display name for the organization.</ParamField>

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/organizations \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{ "name": "Acme" }'
```

```json Response theme={"system"}
{
  "organization": { "id": "org_01H8AA...", "object": "organization", "name": "Acme", "balance_micro_usd": 0, "created_at": "2026-08-20T17:00:00Z" },
  "api_key": { "id": "key_01H8AB...", "object": "api_key", "name": "Acme", "prefix": "sk_live_9f2c", "scopes": ["admin"], "org_id": "org_01H8AA...", "mode": "live", "secret": "sk_live_9f2c...redacted", "created_at": "2026-08-20T17:00:00Z" }
}
```

The reply carries **two** objects: the organization and the first `admin` key that opens it, whose `secret` is returned here and never again. A caller signed in as a person also becomes the founding `owner`.

## List, retrieve & update

```bash theme={"system"}
GET   /v1/organizations         # list the orgs the caller belongs to
GET   /v1/organizations/{id}    # retrieve
```

`GET /v1/organizations` is **orgless** — it answers on a user session rather than an org-scoped key, which is what makes org switching possible. See [Authentication](/docs/api/authentication).

```json Response theme={"system"}
{ "id": "org_01H8AA...", "object": "organization", "name": "Acme", "balance_micro_usd": 48250000, "created_at": "2026-08-20T17:00:00Z" }
```

### Update an organization

`PATCH /v1/organizations/{id}`

<ParamField body="name" type="string" required>New display name.</ParamField>

Returns the updated organization object.

## Members

Users belong to an organization with a role that controls what they can do.

| Role     | Can do                                                        |
| -------- | ------------------------------------------------------------- |
| `owner`  | Everything, including billing, members, and deleting the org. |
| `admin`  | Manage agents, computers, skills, keys, and view billing.     |
| `member` | Create and run agents within the org's resources.             |

### The member object

<ResponseField name="id" type="string">Member id (e.g. `usr_01H...`). It is the id the member routes address and the id the [audit log](/docs/api/audit-logs) records as the actor.</ResponseField>
<ResponseField name="email" type="string">The member's email address.</ResponseField>
<ResponseField name="role" type="string">`owner | admin | member`.</ResponseField>
<ResponseField name="invited_by" type="string | null">Id of the member who invited them; `null` for the founding owner.</ResponseField>
<ResponseField name="created_at" type="string">When the member joined the organization.</ResponseField>

### List members

`GET /v1/organizations/{id}/members` — cursor-paginated. See [Pagination](/docs/api/pagination).

```json Response theme={"system"}
{
  "data": [
    { "id": "usr_01H...", "email": "owner@acme.com", "role": "owner", "invited_by": null, "created_at": "2026-06-01T09:00:00Z" },
    { "id": "usr_01H...", "email": "alice@acme.com", "role": "admin", "invited_by": "usr_01H...", "created_at": "2026-08-20T17:00:00Z" }
  ],
  "has_more": false,
  "next_cursor": null
}
```

### Add a member

`POST /v1/organizations/{id}/members`

<ParamField body="email" type="string" required>Email address to invite.</ParamField>
<ParamField body="role" type="string" required>`owner | admin | member`.</ParamField>

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/organizations/org_01H8AA.../members \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{ "email": "alice@acme.com", "role": "admin" }'
```

```json Response theme={"system"}
{ "id": "usr_01H9JJ...", "object": "member", "email": "alice@acme.com", "role": "admin", "invited_by": "usr_01H...", "created_at": "2026-08-20T17:00:00Z" }
```

### Update a member's role

`PATCH /v1/organizations/{id}/members/{member_id}`

<ParamField body="role" type="string" required>`owner | admin | member`.</ParamField>

### Remove a member

`DELETE /v1/organizations/{id}/members/{member_id}`

Both mutations are recorded in the [audit log](/docs/api/audit-logs) as `member.role_changed` and `member.removed`, attributed to the acting principal.

<Warning>
  **Removal does not cascade.** It deletes the membership row and nothing else. API keys that member created keep working and sessions running under them keep running — revoke each one you want stopped with `DELETE /v1/api_keys/{id}`, reading `GET /v1/api_keys` first, because a key's `created_by` is the only record of who minted it. Agents, skills and files are org-owned and remain. Re-inviting the same email creates a new member.
</Warning>

## API keys

Programmatic access uses org-scoped API keys, managed under `/v1/api_keys`. For creation, scopes, and rotation, see [Authentication](/docs/api/authentication).

```bash theme={"system"}
POST   /v1/api_keys              # create
GET    /v1/api_keys              # list (cursor-paginated)
POST   /v1/api_keys/{id}/rotate  # rotate
DELETE /v1/api_keys/{id}         # revoke
```

<Warning>
  Only `owner` and `admin` members (or a key with the `admin` scope) can manage members and API keys.
</Warning>

<Card title="Credits & billing" icon="credit-card" href="/docs/api/credits">
  The org's real-USD balance, ledger, and top-ups.
</Card>
