API keys

Everything the interface does with transfers can be driven over HTTP. That is what the REST API and the MCP server use, and both authenticate the same way.

Creating one

Account → API keys → Create key. Give it a name you will recognise later, optionally an expiry (up to a year), and choose whether it is read-only.

The key is shown once. The database stores only a SHA-256 hash, so it cannot be shown again and a leaked database yields no usable keys. Copy it now or create another.

Send it in the x-api-key header:

curl -H "x-api-key: $KEY" https://transfer.example.com/api/transfers

What a key can and cannot do

A key acts as the person who created it and carries exactly their permissions. It is a delegated credential, never an escalation.

Two limits are deliberate:

  • A key can never administer users. Admin endpoints refuse API keys outright, even one belonging to an administrator. Those endpoints create and delete accounts, and deleting a user takes every transfer they ever sent with them — while long-lived credentials have a way of ending up in CI configs. Account administration stays something a person does while signed in.
  • A key can never create, list or revoke another key. Otherwise one leaked key could mint a permanent replacement for itself, and revoking the original would achieve nothing.

Read-only keys

Mark a key read-only and it can list and read transfers but not create, send or delete them. Over REST that is enforced on the HTTP method; over MCP each mutating tool declares itself, because a tool call is a POST whatever it does.

Use one for anything that only reports — a dashboard, a backup inventory, a monitoring check.

Revoking

Account → API keys → Revoke. It takes effect on the very next request. There is no cache to wait out.

Keys record when they were last used, which is usually enough to tell whether one is still wanted before you remove it.

Why not bcrypt

Because these are not passwords. An API key is 32 bytes from the system's random generator, so there is no dictionary and no plausible brute force — which is the only thing a slow hash buys. Meanwhile bcrypt would cost about 100 ms of CPU on every authenticated request, which for an interface designed for agents making repeated calls is a large permanent tax for no gain.

Passwords in this app are bcrypt-hashed, as they should be. Tokens are not passwords, and the same tools do not fit both.