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

# Work in a team

> Where your team's quota comes from, what borrowing means, and how to get one run past a budget.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -H "Authorization: Bearer $TUNEPLANE_CLIENT_TOKEN" \
  https://tuneplane.your-company.com/api/teams/mine
```

Lists the teams you belong to. The console shows the same under
[Approvals](/en/console/approvals) and, for an administrator, on
[Teams](/en/console/teams).

Teams are a commercial-edition feature. On a community deployment everyone is in one team and
nothing on this page fires.

## Your run's team comes from its project

Not from you. A run's team is taken from its **project** and stamped on the job at submission.

Both halves matter. A person can belong to several teams, so charging a run to all of them would
count it several times. And a project moving between departments must not rewrite last quarter's
usage, which is why the team is frozen on the row rather than joined at read time.

So changing which team a run counts against means moving the project:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -X PUT https://tuneplane.your-company.com/api/teams/project \
  -H "Authorization: Bearer $TUNEPLANE_CLIENT_TOKEN" \
  -d '{"project": "support-agent", "team": "ml-platform"}'
```

## Two quotas, and they read differently

Your own quota and your team's quota are both checked. Two of the team semantics invert against
the per-user ones, and it is worth knowing which:

|                  | Per-user quota             | Team quota                                                                             |
| ---------------- | -------------------------- | -------------------------------------------------------------------------------------- |
| Not configured   | Treated as a limit of zero | Treated as **no limit**. Creating a team must not become a way to block everyone in it |
| An administrator | Exempt in some checks      | **Not exempt.** Skipping a team's budget spends another department's money             |

Over a team quota, a job waits in `QUEUED` with a `teamQuota` reason, exactly like a personal
quota. The message names the team.

## Borrowing, and what can be taken back

With preemption enabled and a licence, a team quota stops being a ceiling and becomes a
**guarantee**. Your team can run above it when the cluster is idle, and the overflow is
reclaimable.

Five conditions all have to hold before a running job of yours is reclaimed:

* The taker's team is **under** its own guarantee. Otherwise this is one team outbidding another.
* Your team is **over** its guarantee. Otherwise reclaim would break the only promise a quota
  makes.
* It is a different team.
* Your job has been running past a minimum runtime. Otherwise a busy queue reclaims, admits and
  reclaims all day, and the cluster spends its time writing checkpoints.
* Your job holds the card series that is short.

Reclaim takes the **youngest** job first, deliberately not the lowest priority — priority orders
who runs next, which is a different question with the same number.

A reclaimed job is paused with its checkpoint kept, and goes back to `QUEUED`. See
[job states](/en/concepts/job-lifecycle).

## Get one run past a budget

When your team has spent its monthly budget, a queued job says so. Ask for an exception for that
one run:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -X POST https://tuneplane.your-company.com/api/approvals/budget-exception \
  -H "Authorization: Bearer $TUNEPLANE_CLIENT_TOKEN" \
  -d '{"run_id": "run-4f2a91", "reason": "customer demo on Thursday"}'
```

An approval lets **one run** past **one gate**, once. Not the team, not the month, and not until
somebody revokes it. A grant that outlives the reason it was given stops being a budget.

Escalation is opt-in, which is why this is a request you make rather than something every denial
triggers. Team quota usually clears within minutes as colleagues' jobs finish, so routing every
denial to a human would produce a queue of requests that answer themselves and an approver who
stops reading the list.

## Ask for more quota

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -X POST https://tuneplane.your-company.com/api/approvals/quota-change \
  -H "Authorization: Bearer $TUNEPLANE_CLIENT_TOKEN" \
  -d '{
    "subject": "alice", "subject_type": "user",
    "max_concurrent_gpus": 16, "daily_gpu_hours": 200,
    "reason": "Q4 eval sweep"
  }'
```

`subject_type` is `user` or `team`. Fields you leave out are left alone.

There is one level of approval and no chains. Counter-signing belongs to whatever approval system
your organisation already runs; what this owes it is a record and a webhook.

## Track a request

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -H "Authorization: Bearer $TUNEPLANE_CLIENT_TOKEN" \
  "https://tuneplane.your-company.com/api/approvals?status=pending"
```

`status` is `pending`, `approved`, `rejected` or `expired`. Every request carries an expiry, so a
request nobody answered does not sit open forever.

## Check it worked

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -H "Authorization: Bearer $TUNEPLANE_CLIENT_TOKEN" \
  https://tuneplane.your-company.com/api/teams/ml-platform/usage
```

Shows your team's hours for the period. After an approval, the job that was held leaves `QUEUED`
on the next scheduler pass without you resubmitting.

The decision moves the request to its terminal state **before** anything is applied, so two
approvers clicking at once produce one effect.

## Next

[Approvals page](/en/console/approvals) · [Resources and quota](/en/concepts/resources) ·
[Setting up teams](/en/ops/teams) if you are the administrator
