The API
Open, anonymous, no auth. Every note is AES-256-GCM encrypted with a key the server never stores — see how it works for the full model.
Body is the raw payload — text or file bytes. Everything else is headers, so it works cleanly
with curl --data-binary.
| Header | Meaning |
|---|---|
| Content-Type | Real MIME type of the payload |
| X-Password | Optional password; required again to read |
| X-Views | Max number of reads (default 1) |
| X-TTL-Minutes | Expire after N minutes regardless of views |
| X-Allowed-IP | Exact IP, IPv4 CIDR, or comma-separated list |
| X-Filename | Original filename, for downloads |
| X-Encrypted: client | Payload is already encrypted by the caller; stored opaquely |
curl -X POST https://api.whisper.beer/notes \
-H "Content-Type: application/pdf" \
-H "X-Filename: contract.pdf" \
-H "X-Password: hunter2" \
-H "X-Views: 5" \
-H "X-TTL-Minutes: 60" \
--data-binary @contract.pdfEach successful read burns one view. A wrong key/password or a blocked IP never consumes a view, so a guesser can't burn the real recipient's read.
?key=... — server decrypts for you
curl "https://api.whisper.beer/notes/ID?key=KEY" -o downloaded_file
curl "https://api.whisper.beer/notes/ID?key=KEY" -H "X-Password: hunter2"no key — raw ciphertext, decrypt locally
The key never has to travel to the server on read. Response headers carry the crypto metadata needed to decrypt offline.
curl -D headers.txt -o note.enc "https://api.whisper.beer/notes/ID"| Response header | Meaning |
|---|---|
| X-Views-Remaining | Views left after this read |
| X-Alg | aes-256-gcm |
| X-Iv, X-Auth-Tag | Base64url GCM parameters, for local decryption |
| X-Salt | Present if a password was set; needed to re-derive the key |
- IP allowlist supports exact match or IPv4 CIDR — no IPv6 CIDR yet.
- No auth, no rate limiting — it's anonymous by design.
- Request/response bodies are capped around 4MB per note or file.