Self-hosted receipt, invoice, and document management for self-employed professionals.
Receiptory is a single-container web application that ingests receipts and invoices from multiple channels — web upload, email, Telegram, mobile camera, or a watched folder — and uses LLM-powered extraction to automatically classify, parse, and organize them. Built for running on a home NAS or any Docker host.
| Dashboard | Document Browser |
|---|---|
![]() |
![]() |
| Document Detail | Administration |
|---|---|
![]() |
![]() |
| Dark Mode |
|---|
![]() |
Ingestion
- Drag-and-drop web upload with SHA-256 deduplication
- Mobile document scanner with live boundary detection and perspective correction
- Telegram bot — forward photos or documents for automatic processing
- Gmail IMAP polling — auto-ingests receipts from labeled emails
- Watched folder — drop files on a network share for hands-free ingestion
Processing
- LLM-powered extraction via litellm (Gemini, OpenAI, Anthropic, and more)
- Single-pass OCR, field extraction, and classification
- Automatic vendor detection, amount parsing, tax ID matching
- Confidence scoring with human review queue for uncertain extractions
Management
- Document browser with full-text search (SQLite FTS5), filters, sorting, and pagination
- Document detail view with rendered page images and editable metadata
- Custom categories with LLM-guided classification
- Export to ZIP with category-organized PDFs, CSV, and Excel metadata
Infrastructure
- Cloud backup to Google Drive and/or OneDrive via OAuth (or any rclone remote)
- Configurable retention policy (daily/weekly/monthly/quarterly)
- Dark theme with light/dark/system toggle
- Notification alerts via Telegram and email
- Single-user session auth
git clone https://github.com/LevMuchnik/Receiptory.git
cd Receiptory
cp .env.example .env
# Edit .env — at minimum set RECEIPTORY_LLM_API_KEY
docker compose up -d --buildOpen http://localhost:8484. Log in with admin / admin, then change the password in Administration > General.
-
Open the Unraid terminal (or SSH into your server)
-
Clone and configure:
mkdir -p /mnt/user/appdata/Receiptory cd /mnt/user/appdata/Receiptory git clone https://github.com/LevMuchnik/Receiptory.git . cp .env.example .env nano .env
-
Set at minimum:
RECEIPTORY_LLM_API_KEY=your-api-key-here RECEIPTORY_LLM_MODEL=gemini/gemini-3-flash-preview RECEIPTORY_AUTH_USERNAME=admin RECEIPTORY_AUTH_PASSWORD=your-password RECEIPTORY_PORT=8484
-
Build and start:
docker compose up -d --build
-
Open
http://your-nas-ip:8484
cd /mnt/user/appdata/Receiptory
git pull
docker compose up -d --buildAll data lives in data/ (mounted as a Docker volume) and survives container rebuilds:
| Path | Contents |
|---|---|
data/receiptory.db |
SQLite database |
data/storage/ |
Document files (originals, converted, filed) |
data/logs/ |
Application logs |
data/rclone.conf |
Cloud backup credentials (auto-generated) |
All settings are configurable via the admin UI. Environment variables take precedence over database values.
| Variable | Default | Description |
|---|---|---|
RECEIPTORY_LLM_API_KEY |
— | API key for your LLM provider (required) |
RECEIPTORY_LLM_MODEL |
gemini/gemini-3-flash-preview |
litellm model string |
RECEIPTORY_AUTH_USERNAME |
admin |
Web UI username |
RECEIPTORY_AUTH_PASSWORD |
admin |
Web UI password |
RECEIPTORY_SECRET_KEY |
(random) | Session signing key — set for persistent sessions across restarts |
RECEIPTORY_PORT |
8484 |
HTTP port |
RECEIPTORY_DATA_DIR |
./data |
Data directory (DB, files, logs) |
RECEIPTORY_TELEGRAM_BOT_TOKEN |
— | Telegram bot token from @BotFather |
RECEIPTORY_GMAIL_ADDRESS |
— | Gmail address to poll via IMAP |
RECEIPTORY_GMAIL_APP_PASSWORD |
— | Gmail App Password (16 chars) |
RECEIPTORY_BACKUP_SCHEDULE |
0 2 * * * |
Backup cron schedule |
RECEIPTORY_THEME |
light |
Default theme: light, dark, or system |
RECEIPTORY_DEV |
0 |
Set to 1 when running with Vite dev server |
See .env.example for the full list with descriptions.
- Message @BotFather on Telegram, send
/newbot - Copy the bot token to
.envasRECEIPTORY_TELEGRAM_BOT_TOKEN(or set via Administration > Telegram) - Restart the container
- Optionally restrict access by adding your Telegram user ID (message @userinfobot to find it)
- Send or forward photos/documents to your bot
Uses IMAP with a Gmail App Password — no Google Cloud project needed.
- Enable 2-Step Verification: myaccount.google.com/security
- Generate an App Password: myaccount.google.com/apppasswords
- In Administration > Email, enter the Gmail address and App Password
- Click Test Connection to verify
Unread emails with PDF/image attachments are ingested automatically. HTML-only emails (e.g., digital receipts) are converted to PDF. Emails from unauthorized senders are flagged for review.
On Android devices, the app opens directly to a camera-based document scanner with:
- Live document boundary detection and perspective correction
- Image enhancement (contrast, brightness optimization)
- Multi-page scanning with PDF assembly
- Requires HTTPS or a Chrome flag for camera access on LAN
Set RECEIPTORY_WATCHED_FOLDER_PATH to a directory. Files dropped there are auto-ingested and moved to a processed/ subfolder.
Receiptory backs up to Google Drive and/or OneDrive via OAuth. Both can be active simultaneously. Backups are scheduled via cron and include the database, all document files, and metadata exports (JSONL + CSV + Excel).
- Go to Google Cloud Console > Credentials
- Create a project if needed, then + Create Credentials > OAuth client ID
- Configure consent screen (External), select Web application
- Add redirect URI:
http://your-nas-ip:8484/api/cloud-auth/callback/gdrive - Copy Client ID and Secret
- Enable the Google Drive API
- In Receiptory: Administration > Resilience > paste credentials > Connect Google Drive
- Go to Azure Portal > App registrations
- + New registration — select "Accounts in any organizational directory and personal Microsoft accounts"
- Add redirect URI (Web):
http://your-nas-ip:8484/api/cloud-auth/callback/onedrive - Copy Application (client) ID
- Under Certificates & secrets, create a new secret and copy the Value
- Under API permissions, add:
Files.ReadWrite.All,User.Read,offline_access - In Receiptory: Administration > Resilience > paste credentials > Connect OneDrive
| Type | Schedule | Default Retention |
|---|---|---|
| Daily | Every day | 7 days |
| Weekly | Sundays | 4 weeks |
| Monthly | 1st of month | 3 months |
| Quarterly | Jan/Apr/Jul/Oct 1st | Never deleted |
Configurable in Administration > Resilience > Backup Schedule.
- Python 3.12+, uv
- Node.js 20+
uv sync --all-extras
RECEIPTORY_DEV=1 uv run uvicorn backend.main:create_app --factory --reload --port 8484cd frontend && npm install && npm run devThe Vite dev server runs on port 5173 with API proxy to localhost:8484. Set RECEIPTORY_DEV=1 to prevent the backend from serving static files.
uv run pytest tests/ -vcd frontend && npm run build && cd ..
uv run uvicorn backend.main:create_app --factory --host 0.0.0.0 --port 8484receiptory/
├── backend/
│ ├── main.py # FastAPI app factory, lifespan
│ ├── config.py # Settings (env > db > defaults)
│ ├── auth.py # Session-based authentication
│ ├── database.py # SQLite WAL + migration runner
│ ├── storage.py # File I/O, page rendering
│ ├── models.py # Pydantic request/response models
│ ├── api/ # REST endpoint routers
│ ├── processing/ # LLM extraction pipeline, queue
│ ├── ingestion/ # Telegram bot, Gmail poller, watched folder
│ ├── backup/ # Scheduler, runner, rclone, OAuth
│ └── notifications/ # Telegram + email notification dispatch
├── frontend/src/
│ ├── pages/ # Page components
│ ├── components/ # Reusable UI components
│ ├── components/scanner/ # Mobile document scanner
│ ├── contexts/ # Auth + Theme providers
│ └── lib/ # API client, hooks, utilities
├── migrations/ # Numbered SQL migration files
├── tests/ # pytest test suite
├── Dockerfile # Multi-stage build
└── docker-compose.yml
| Layer | Technology |
|---|---|
| Backend | Python 3.12, FastAPI, SQLite (WAL + FTS5), litellm |
| Frontend | React 18, TypeScript, Vite, Tailwind CSS v4, shadcn/ui |
| Document processing | PyMuPDF, Pillow, WeasyPrint |
| Mobile scanner | Scanic (WASM), jsPDF |
| Cloud backup | rclone (Google Drive, OneDrive, S3, etc.) |
| Deployment | Docker Compose, single container |
This project is licensed under the GNU Affero General Public License v3.0.




