# Cloudish credits

Everything to do with credits: which key to use, what to do when one runs
out, claiming, and moving credits between keys. Every call
authenticates with `Authorization: Bearer $CLOUDISH_API_KEY`, same as
SKILL.md.

## One key per project folder

Keep one API key per project folder, stored in that folder's gitignored
`.env` — reuse it if it's already there instead of minting a new one:

```bash
grep -q '^CLOUDISH_API_KEY=' .env 2>/dev/null || {
  RESPONSE=$(curl -s -X POST https://cloudish.ai/api/v1/keys -H "content-type: application/json" \
    -d "{\"alias\": \"$(basename "$PWD")\"}")
  printf 'CLOUDISH_API_KEY=%s\nCLOUDISH_ALIAS=%s\n' \
    "$(echo "$RESPONSE" | jq -r .key)" "$(echo "$RESPONSE" | jq -r .apiKey.alias)" >> .env
}
grep -qxF .env .gitignore 2>/dev/null || echo .env >> .gitignore
```

(Uses `jq` to read the response; parse it however you like if it isn't
installed.)

Why per folder: each project spends only its own balance, so one project
running out never stops another, and the key's alias (named after the
folder) tells a human at a glance which project a key, a claim link or a
`402` is about. Several projects deployed from the same folder can share
its key.

The requested alias is best-effort: lowercase letters, digits and hyphens
(anything else is normalized), and if it's taken you get a close variant
such as `todo-app-otter`. Always use the `apiKey.alias` that comes back.

## Checking a balance

```bash
curl https://cloudish.ai/api/v1/credits -H "Authorization: Bearer $CLOUDISH_API_KEY"
# -> { "balance": 4.2, "granted": 5, "spent": 0.8, "quota": null, "spentToday": 0.3 }
```

`GET /api/v1/credits/transactions` lists every grant, transfer and usage
charge behind that number.

## When a key runs out

A request billed to an out-of-credit key returns `402` with the alias of
the key that ran out:

```json
{ "error": "This API key is out of credits — ...", "alias": "todo-app", "docs": "https://cloudish.ai/credits.md" }
```

The alias names the key that pays for the request — yours, for anything you
call with your own key — so match it against `CLOUDISH_ALIAS` in `.env`
to know which folder's key needs credits. Then, from that folder, mint a claim link
for that key yourself and hand your human the URL itself — never the
command to mint one — naming the alias so they know which project it's for:

```bash
curl https://cloudish.ai/api/v1/credits/claim -H "Authorization: Bearer $CLOUDISH_API_KEY"
# -> { "url": "https://..." }
# -> tell your human: "This adds credits to todo-app (this folder's key): https://..."
```

The link is single-use and expires 30 minutes after it's minted, so mint it
right when you hand it over and say that it expires; if your human comes
back later or the page says it expired, mint a new one. Where
the credits come from — a purchase, or a balance your human already holds
elsewhere — is handled on that page; you never need any key other than this
folder's own, so don't ask your human for one.

A `402` saying a daily usage quota was reached is different: the key still
has credits but hit its rolling 24-hour quota, and recovers on its own as
that window moves.

## Moving credits between keys

Any key can send part of its balance to another key by alias, no admin
needed — authenticate as the sending key:

```bash
curl -X POST https://cloudish.ai/api/v1/credits/transfer \
  -H "Authorization: Bearer $SENDING_API_KEY" -H "content-type: application/json" \
  -d '{"to": "todo-app", "amount": 5}'
# -> { "ok": true, "balance": <sender's remaining balance>, "reference": "<ledger id>" }
```

Only do this with keys you already hold (e.g. two of your own project
folders) and when your human asks — moving credits is their call.

## Keys under a key

Minting a key *with* a key makes it a child of that key — same account, no
free grant of its own. Hand one to a sub-agent or a runtime that needs its
own credential, and fund it by transfer:

```bash
curl -X POST https://cloudish.ai/api/v1/keys -H "Authorization: Bearer $CLOUDISH_API_KEY" \
  -H "content-type: application/json" -d '{"alias": "todo-app-worker"}'
# -> { "apiKey": { "alias": "todo-app-worker", "parent": "todo-app", ... }, "key": "cld_..." }
```

A key or any key above it can delete it (`DELETE /api/v1/keys/{alias}`):
it and everything under it stop working, and their balances move to its
parent. Root keys (no parent) can't be deleted.

A key above another can cap what crosses that key's boundary — received
from anywhere outside it, including its parent, or sent outside it — in any
rolling window, in seconds. A key can never set its own limits:

```bash
curl -X PUT https://cloudish.ai/api/v1/keys/todo-app-worker/limits -H "Authorization: Bearer $CLOUDISH_API_KEY" \
  -H "content-type: application/json" \
  -d '{"limits": [{"direction": "receive", "windowSeconds": 3600, "max": 5}]}'
```

Transfers between keys of one account don't count against the account's own
limits, only those it set on the keys involved. A transfer, purchase or claim
over a limit answers `429` with `code: "limit_exceeded"`, the key whose
limit it was, the window, and what's left. `GET /api/v1/credits/flow` shows
what your key received and sent in the last hour, day and 30 days, and what
it may still receive or send outside its account.
