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

# App authentication

> Configure your app key and authenticate server calls to Bankroll.

Your server authenticates [notifications](/build/push) and [matchmaking](/build/matchmaking)
with an app-generated keypair. Bankroll attests its public key in your
[verified manifest](/build/verified); the private key stays with your app.

## Configure the app key

Set **`BANKROLL_APP_KEY`** on your server to a base58 Ed25519 secret key:
64 bytes containing the 32-byte seed followed by its matching public key.
Keep it separate from `BANKROLL_TREASURY_KEY`. The app key authenticates API
calls; it does not receive payments or sign payouts.

`appAddress()` from `@joinbankroll/sdk/server` derives the public key.
`manifestRoute` includes it as top-level `appKey` by default. The SDK selects
`BANKROLL_APP_KEY`, falling back to **`BANKROLL_PUSH_KEY` only when the app
key variable is unset**. An empty, malformed, or mismatched selected key fails;
it does not fall back to another key or the treasury.

Have Bankroll sign the manifest containing `appKey`, then set
`BANKROLL_SIGNED_MANIFEST` to that signed JWT. `manifestRoute` serves it verbatim.
Notifications also need a truthy [`capabilities.push`](/build/manifest#app-key-and-notifications);
matchmaking requires no additional capability.

## Authenticate requests

Your server signs a short-lived JWT with the private key matching the signed
manifest's `appKey`. For app-authenticated endpoints at
`https://api.joinbankroll.com`, send `Authorization: Bearer <app-jwt>`.
The protected header is:

```json theme={null}
{ "alg": "EdDSA", "typ": "bankroll-app-auth+jwt" }
```

| Claim | Value                                                                               |
| ----- | ----------------------------------------------------------------------------------- |
| `iss` | Your exact canonical public HTTPS origin: no path, trailing slash, or default port. |
| `aud` | The string `bankroll-api`.                                                          |
| `iat` | Integer issuance time in Unix seconds.                                              |
| `exp` | Integer expiry in Unix seconds, after `iat` and at most 60 seconds later.           |

Bankroll allows five seconds of clock tolerance. The JWT must be at most
8 KiB. It authenticates app identity and can be reused across endpoints during
its lifetime; each endpoint checks its own permissions and JSON body. The SDK
creates these tokens for you. User session tokens and legacy notification-request JWTs
do not authenticate these general API calls.

Authentication failures return `{ "error": "unauthenticated" }` (401),
`app_not_verified` (403), or `unavailable` (503). Each endpoint documents its
additional errors.

## Rotate a key

Generate a replacement keypair in your app's environment and have its public
key re-signed in the manifest. Serve the replacement signed manifest and switch
to its matching private key. A changed manifest re-asks connected users for consent.

Bankroll caches verified manifests for five minutes. After changing a key,
serve a newly signed manifest and allow for that cache window; replacing or
removing the manifest does not promise immediate revocation of prior access.

## Legacy keys

Existing manifests with a public-key string in `capabilities.push` remain
supported. `pushAddress()` returns the same selected public key as
`appAddress()`, so existing `push: pushAddress` declarations still work.
If `appKey` is present, it must be valid and takes precedence over that legacy
string. Without `appKey`, notifications need a valid legacy key string; a boolean
alone is insufficient.

With only `BANKROLL_PUSH_KEY`, the SDK preserves the legacy notification request
format. Setting `BANKROLL_APP_KEY` selects shared app authentication for
notifications. Before switching, get a new signed manifest containing its `appKey`
and serve it verbatim. An existing signed manifest is never rewritten by
an SDK upgrade. See [notification requests](/build/push#authentication).
