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

# Key expiry and rotation

> How long a key lives, what an expired key answers, the reminder emails, and how a computer’s own key rotates.

A key you make can expire on a date. Orgo emails you before it does. Orgo never rotates a key you make, because it can't reach where you keep it. A computer's own key is the exception: Orgo wrote it onto the computer, so Orgo changes it there by itself.

## Expiry choices

Pick an expiry when you create a key, in Settings or with the API. A new key in Settings defaults to 90 days.

| In Settings | With the API |
| - | - |
| 7 days | `"expires_in_days": 7` |
| 30 days | `"expires_in_days": 30` |
| 90 days | `"expires_in_days": 90` |
| 1 year | `"expires_in_days": 365` |
| Custom date | `"expires_at": "2027-03-03T00:00:00Z"`, at most a year away |
| No expiration | Send neither field |

`expires_in_days` takes any whole number from `1` to `365`. A custom date in Settings expires at the end of that day in your browser's time zone. A key that never expires works until you delete it.

The keys Orgo makes for its own apps and for a computer never expire.

A key that expires can make keys only if they expire no later than it does. This is part of [the no-escalation rule](/api-reference/api-keys/manage.md#the-no-escalation-rule).

## Change or remove an expiry

Send a new `expires_in_days` or `expires_at` to `PATCH /api/keys/{id}`. Send `"expires_at": null` to remove the expiry. The key's value does not change.

```bash theme={null}
curl -X PATCH https://www.orgo.ai/api/keys/$KEY_ID \
  -H "Authorization: Bearer $ORGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "expires_in_days": 30 }'
```

A changed expiry gets its own reminder emails.

## When a key expires

Past `expires_at`, the key signs in nowhere: not the API, not the Desktop API proxy, not a WebSocket. An HTTP request answers `401`:

```json theme={null}
{
  "error": "This API key expired on Mar 3, 2026. Make a new one in Settings, API keys.",
  "code": "key_expired"
}
```

Every endpoint answers this way. A WebSocket closes with code `4001` and the reason `Authentication required`.

The date is in UTC. An expired key's `last_used` stays at its last real use. The key stays in your list, with `"expired": true`, until you delete it. A one-computer key is the exception: Orgo deletes it soon after it expires. Make a new key and switch to it.

### What a key that expires can't do

A key that expires can't hand out more time than it has. The keys it makes or changes expire no later than it does, and it can't remove or push back its own expiry, as described in [The no-escalation rule](/api-reference/api-keys/manage.md#the-no-escalation-rule). It never causes a computer key to be made, since a computer key never expires. And it can't control a computer that already holds one, as described in [Computers that hold their own key](/api-reference/api-keys/permissions.md#computers-that-hold-their-own-key).

## Reminder emails

Orgo emails the account's address twice before a key you made expires:

* 7 days before.
* 1 day before.

Each email names the key and its prefix, never its value, and links to Settings, API keys. A key made inside a window skips that reminder, so a 7 day key gets only the 1 day email. Changing a key's expiry starts its reminders again. Keys that never expire, and a computer's own key, get no emails.

## Rotate a key you made

Orgo does not rotate the keys you make. To replace one without downtime:

1. Create a new key with the same permissions and scope.
2. Update everything that uses the old key.
3. Revoke the old key with `DELETE /api/keys/{id}`.

Both keys work until you revoke the old one. Give keys an expiry, and make the new one before the old one ends.

## How a computer key rotates

Orgo puts a computer's own key on the computer as `ORGO_API_KEY`: in `/root/.env` when the computer is created, and in `/root/.orgo-secrets.env` every time after. That key changes its secret by itself:

1. The key is used, and its secret is older than 30 days.
2. Before the request runs, Orgo writes a new secret onto the computer, in `/root/.orgo-secrets.env`. It does this only while the computer is running, and never wakes it to do so.
3. Orgo then switches the key to the new secret. The key keeps its id, so Settings and the computer still name the same key.
4. The old secret keeps working for 10 more minutes, for anything that read it before. After that it answers `401` with `{ "error": "Invalid API key" }`.

The key's `rotated_at` field says when its secret last changed.

### When rotation fails

If Orgo can't write the new secret (the computer is not running, can't be reached, or the write fails), the request does not run. It answers `503`:

```json theme={null}
{
  "error": "Key rotation failed, try again later.",
  "code": "key_rotation_failed"
}
```

The answer carries `Retry-After: 5`. Nothing changed, and the old secret still works. The next request tries again. A call made with a computer's own key from outside the computer, while the computer is stopped and the secret is due, keeps failing this way until the computer runs again.

## Re-read the key after a 401

A program on the computer should read `ORGO_API_KEY` when it starts, and read it again when a request answers `401`. Then retry once with the new value. Read `/root/.orgo-secrets.env` first, and `/root/.env` when the first file is missing or has no `ORGO_API_KEY`. In a shell, sourcing `/root/.env` gives the newest value, because it sources `/root/.orgo-secrets.env` last. Both files are readable by `root` only.

Processes that started before a rotation keep the value they read. A long-running program that only reads its environment at start fails 10 minutes after the next rotation.

```bash Bash theme={null}
# Run as root on the computer. Set COMPUTER_ID to the computer's UUID.
call_orgo() {
  . /root/.env
  status=$(curl -s -o /tmp/orgo-out.json -w '%{http_code}' "$@" \
    -H "Authorization: Bearer $ORGO_API_KEY")
  if [ "$status" = "401" ]; then
    . /root/.env   # the key may have rotated: read it again
    status=$(curl -s -o /tmp/orgo-out.json -w '%{http_code}' "$@" \
      -H "Authorization: Bearer $ORGO_API_KEY")
  fi
  cat /tmp/orgo-out.json
}

call_orgo "https://www.orgo.ai/api/computers/$COMPUTER_ID"
```

```python Python theme={null}
# Run as root on the computer. Set COMPUTER_ID to the computer's UUID.
import os
import shlex
import requests

def read_orgo_key():
    for path in ("/root/.orgo-secrets.env", "/root/.env"):
        try:
            with open(path) as f:
                for line in f:
                    line = line.strip()
                    if line.startswith("export "):
                        line = line[len("export "):]
                    name, sep, value = line.partition("=")
                    if sep and name == "ORGO_API_KEY":
                        return shlex.split(value)[0]
        except FileNotFoundError:
            continue
    raise RuntimeError("ORGO_API_KEY is not on this computer")

def call_orgo(method, url, **kwargs):
    for attempt in range(2):
        headers = {"Authorization": f"Bearer {read_orgo_key()}"}
        response = requests.request(method, url, headers=headers, **kwargs)
        if response.status_code != 401:
            return response
    return response  # still 401 with a fresh key: the key was revoked

computer = call_orgo("GET", f"https://www.orgo.ai/api/computers/{os.environ['COMPUTER_ID']}")
print(computer.json())
```

A `401` that persists after a fresh read means the key was revoked, not rotated. A `503` with `code: "key_rotation_failed"` means try again shortly with the same key, after the `Retry-After` seconds.

## Related pages

* [Create and manage API keys](/api-reference/api-keys/manage.md): `expires_in_days` and `expires_at` on create and change.
* [Key scopes and computer keys](/api-reference/api-keys/computer-keys.md): what a computer's own key is and where it comes from.
* [Get an identity token inside a computer](/guides/instance-identity.md): a short-lived token with no key file, coming soon.


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