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

# Authentication

> Authentication: Set Aside Money developer API reference.

The API uses personal API keys. Send one as a bearer token on every request:

```
Authorization: Bearer sam_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

There is no OAuth flow, no client id, and no token exchange. A key belongs to you and acts on your
Set Aside Money account.

## Scopes

A key carries one of two levels of access, chosen when you create it.

| Scope | What it allows |
| - | - |
| `data.read` | Read accounts, transactions, Expenses, funding schedules, categories, settings, analytics, and sync status. |
| `data.read data.write` | Everything above, plus creating and updating transactions, assigning them to Expenses, moving money between Expenses, and triggering a sync. |

Write always implies read. There is no write-only key, because software that can create a
transaction essentially always needs to read it back.

Choose read only unless you have a specific reason not to. You can create a second key later; you
cannot narrow a key after the fact.

## What a key can never do

Some things are deliberately unreachable with a key, no matter its scope:

* Connecting or disconnecting a bank.
* Anything to do with billing or your subscription.
* Adding or removing people from a shared account.
* Deleting an account, or deleting anything at all. There are no `DELETE` endpoints in this version.
* Creating another API key.

These stay in the app, behind a signed-in session.

## Expiry

Every key expires. You choose 30 days, 90 days, or a year when you create it, and a year is the
default. The expiry is shown in the key list and returned by `/v1/me`, so you can warn yourself
before it lapses.

An expired key returns `401`. There is no grace period.

## Rotating a key

There is no in-place rotation, on purpose. To rotate:

1. Create the new key.
2. Deploy it.
3. Confirm the new key is working, using `last_used_at` in the key list.
4. Revoke the old one.

Both keys work during the overlap, so nothing has to be down while you cut over.

## Revoking a key

Revoke from the key list in the app. It takes effect on the very next request, with no caching and
no delay.

Keys are also revoked automatically when:

* **The subscription lapses or drops below Pro.** Access is re-checked on every single request, so a
  downgrade stops a key immediately rather than at some later sweep.
* **You leave a shared account.** A key stops working for an account you are no longer a member of.
* **The account is deleted.** Everything goes with it.

## How keys are stored

Only a SHA-256 hash of the key is stored. The full value exists in exactly one place: the response
that created it. Nobody at Set Aside Money can read your key back to you, which is why losing one
means creating another.

If a key leaks, revoke it in the app first and then create a replacement. Revocation is immediate,
so that ordering costs you nothing and closes the window straight away.

## Errors you will see

| Status | Code | What happened |
| - | - | - |
| `401` | `missing_api_key` | No `Authorization` header. |
| `401` | `invalid_api_key` | Unknown, revoked, expired, or the account lost developer API access. |
| `401` | `invalid_credential_context` | The request also carried a browser session cookie. Send the key on its own. |
| `403` | `insufficient_scope` | The key is read only and the endpoint writes. |
| `403` | `plan_upgrade_required` | The account is not on Pro or Max. |

`invalid_api_key` is deliberately one message for several causes. Distinguishing "expired" from
"never existed" would tell someone probing for keys which ones are real.


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