# Manage Accounts

> Switch between local accounts, create new ones, move stablecoins with pay push, and back up or restore the secret key.

`pay` can hold any number of accounts per network. One is the **active** account at a time; everything else sits idle in `~/.config/pay/accounts.yml` until you switch.

The secret keys themselves live in the OS secure store (Apple Keychain on macOS, GNOME Secret Service on Linux, Windows Hello on Windows). `accounts.yml` only records the public key, network, and which backend the secret is stored in.

## The default account

`pay` picks the account to sign with using a three-step rule:

1. An explicit `--account <name>` flag wins.
2. The `PAY_ACTIVE_ACCOUNT` environment variable wins next.
3. Otherwise: the account with `active: true` in `~/.config/pay/accounts.yml` for the current network. If no account is marked active, `pay` falls back to the first one alphabetically.

```sh
pay whoami                # which account is active right now
pay account list          # every account on every network, with balances
```

`pay whoami` lists the active mainnet account and its non-zero stablecoin balances. `pay account list` is broader — it shows all accounts on all networks.

## Create a new account

```sh
pay account new work
pay account new trading --backend keychain --force
```

| Flag        | Description                                                                         |
| ----------- | ----------------------------------------------------------------------------------- |
| `<NAME>`    | Required. Account name — used in `--account work`.                                  |
| `--backend` | `keychain`, `gnome-keyring`, or `windows-hello`. Defaults to the OS native backend. |
| `--force`   | Replace an existing account with the same name.                                     |

The new account's secret lives in the OS secure store; only the public key is written to `accounts.yml`. See [Configuration → accounts.yml](/docs/toolchain/configuration#accountsyml) for the full schema.

## Make an account the default

```sh
pay account default work
```

Sets `active: true` on `work` and clears it from every other account on the same network. Subsequent payment commands sign with `work` until you switch again or pass `--account <other>`.

## Switching for a single call

You don't have to flip the default to send from a different account once:

```sh
pay --account trading push 5 work
pay --account trading whoami
```

`--account` is a global flag, so it precedes the subcommand.

## Move stablecoins between accounts

`pay push` (alias for `pay send`) transfers a stablecoin from the active account to any other Solana address — including one of your other local accounts.

```sh
pay push 1.25 trading                  # send 1.25 USDC to local account "trading"
pay push 50 <recipient-base58>         # send to any Solana address
pay push 1 trading --currency USDC     # be explicit about the stablecoin
pay push max trading                   # drain the active account
pay push 1 trading --fee-within        # take the fee out of the 1 USDC, not on top
```

`pay` resolves a recipient that matches a local account name to its public key. The transfer itself is a normal on-chain SPL transfer — there's no special path for "local-to-local."

### Fees

`pay push` uses a **fee-payer–backed** flow: the pay-api co-signer pays the Solana network fee. You never need SOL in the sending account.

| What you pay                                                                          | How                                                                                |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| A small **stablecoin** processing fee (typically ≤ 0.0015 USDC).                      | Added on top of `AMOUNT` by default; subtracted from `AMOUNT` with `--fee-within`. |
| Recipient ATA rent, when the recipient has never held that stablecoin (~0.00204 SOL). | Sponsored by pay-api and surfaced as part of the same stablecoin fee.              |
| Solana network fee (lamports).                                                        | Sponsored by pay-api. You pay nothing in SOL.                                      |

The receipt looks like this:

```
Sent 1 USDC to 9pLk… (total paid: 1.0015 USDC, fee: 0.0015 USDC)
  https://explorer.solana.com/tx/5Hk…
```

`pay push max` automatically implies `--fee-within` so the wallet ends at exactly zero balance.

### Picking the currency

When the active account holds multiple stablecoins and you omit `--currency`:

- **One eligible balance** → `pay` uses it silently.
- **More than one** → an interactive picker lists each currency with its available balance.
- **No TTY** (running under an agent or with `NO_DNA=1`) → the command errors and prints the eligible balances. Pass `--currency USDC` explicitly to suppress the prompt.

## Back up an account

To take an account off this machine — or just to have a recovery copy — export its secret to a JSON file you control.

<Callout type="caution">
  An exported JSON file is the full private key. Anyone who reads it can spend the account's stablecoins. Don't email
  it, don't paste it into chat, don't commit it to git.
</Callout>

```sh
pay account export work
pay account export work ./work-backup.json
pay account export work -                       # write to stdout
```

The default path is `./pay-account-<name>-<pubkey>.json`. The file is a single JSON array of 64 unsigned bytes — the standard Solana keypair format:

```json
[32, 144, 200, 5, 87, 12, …, 248, 64, 91]   // 64 bytes total
```

Bytes `[0..32]` are the Ed25519 secret key. Bytes `[32..64]` are the Ed25519 public key. The same format `solana-keygen` uses, so the file works with `solana`, `solana-keygen recover`, or any Solana SDK that takes a 64-byte JSON keypair.

Move the file to a password manager, a hardware-encrypted USB, or wherever you keep other secrets. Then delete the working copy:

```sh
shred -u ./work-backup.json   # Linux
rm -P ./work-backup.json      # macOS
```

## Restore an account

On any other machine, install `pay` (see [Install](/docs/toolchain/install)), then:

```sh
pay account import work ./work-backup.json
```

| Argument    | Description                                                                       |
| ----------- | --------------------------------------------------------------------------------- |
| `<NAME>`    | The local name to register the account under. Doesn't have to match the original. |
| `<FILE>`    | Path to the exported JSON keypair.                                                |
| `--backend` | `keychain`, `gnome-keyring`, or `windows-hello`. Defaults to the OS native.       |

`pay account import` writes the secret to the OS secure store and adds a row to `accounts.yml` with `keystore: <backend>` and the cached pubkey. After that the account behaves like any other — `pay whoami`, `pay push`, `pay --account work …` all work as normal.

## Inspect the macOS Keychain entry directly

On macOS, every account's secret lives as a generic password under service `pay.sh` and account `keypair:<name>`. You can read it back without `pay`:

```sh
security find-generic-password -s "pay.sh" -a "keypair:work" -w
```

The output is a **128-character hex string** — the same 64 bytes that `pay account export` would write, in hexadecimal instead of a JSON array. The first 64 hex characters (32 bytes) are the secret key; the last 64 are the public key.

If you'd rather poke around the GUI: open **Keychain Access.app**, set the keychain to **login**, and search for `pay.sh`. Each `pay` account appears twice:

| Service  | Account          | What it holds                                                  |
| -------- | ---------------- | -------------------------------------------------------------- |
| `pay.sh` | `keypair:<name>` | 64-byte Ed25519 keypair, hex-encoded.                          |
| `pay.sh` | `pubkey:<name>`  | 32-byte public key only, hex-encoded (cached for fast lookup). |

The entries are stored with the `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` flag — they're tied to this Mac and **do not iCloud-sync**. Moving to another Mac requires the export/import flow above.

### Convert the hex dump back to a JSON keypair

If you've got the hex from `security` and want a Solana-CLI-compatible JSON keypair file:

```sh
hex=$(security find-generic-password -s "pay.sh" -a "keypair:work" -w)
python3 -c "
import sys
hx = sys.argv[1]
print('[' + ','.join(str(int(hx[i:i+2], 16)) for i in range(0, len(hx), 2)) + ']')
" "$hex" > work-from-keychain.json
```

The resulting file is identical in shape to what `pay account export` writes.

## Lose access to the Keychain

If the Keychain entry is gone but `accounts.yml` still lists the account, `pay` refuses to sign for it. The fix is the same in either direction:

1. If you have the exported JSON: `pay account import <name> ./work-backup.json` (replaces the broken entry).
2. If you have neither, the funds are gone — that's why the export step is the entire backup story.

## Other backends

- **GNOME Secret Service (Linux)**: inspect via `seahorse` (the Keyring GUI) or `secret-tool lookup service pay.sh account "keypair:<name>"`. Same hex encoding as macOS.
- **Windows Hello**: the secret is sealed to a Windows Credential. There's no plain shell command equivalent to `security find-generic-password`; rely on `pay account export` for backups.

## Remove an account

```sh
pay account remove work          # mainnet, with confirmation
pay account remove dev --sandbox --yes
```

Permanently deletes the OS-secure-store entry **and** the `accounts.yml` row. There's no undo unless you exported a backup first.
