Route voice/transcript captures into an Obsidian vault — atomically, idempotently, with no silent loss.
Project description
voice-to-vault
Turn voice captures into a well-organized Obsidian vault.
Point your transcription service's webhook at voice-to-vault. Each capture is
routed to the right folder by your own rules, written as a clean Markdown
note with front matter — atomically, idempotently, and with a guarantee
it's never silently lost. Then query the result with
vault-ask.
$ echo '{"id":"abc","title":"Client invoice","transcript":"Send the invoice to the client after the meeting."}' \
| voice-to-vault ingest --config config.json
{
"ok": true,
"action": "written",
"note": ".../Work/2026-06-20_10-00-00_abc.md",
"route": "work",
"review_status": "auto-routed",
"confidence": "high"
}
Why
Voice capture is easy; keeping the result organized and trustworthy is the hard
part. voice-to-vault is the small, boring, correct middle layer:
- Config-driven routing — you declare routes (
keywords/aliases→ folder). Generic filler words ("note", "idea", "today") never decide a route on their own, so captures don't all pile into one place. Ties go to needs-review, not a guess. - Atomic writes — a crash mid-write can never leave a half-written note.
- Idempotent + no silent loss — the same capture is never written twice, and if a write fails the capture is not marked done, so it can be retried instead of vanishing while the caller is told "ok".
- Provider-agnostic — maps common webhook payload shapes to a normalized capture.
- Zero dependencies — Python standard library only (the web listener too).
Install
Requires Python 3.9+.
pip install git+https://github.com/guillaumevele/voice-to-vault.git
Configure
Copy examples/config.example.json and edit it:
{
"vault": "~/Obsidian/MyVault",
"default_route": { "name": "inbox", "folder": "00-inbox" },
"routes": [
{ "name": "work", "folder": "Work",
"keywords": ["meeting", "deadline", "invoice"], "aliases": ["project"] },
{ "name": "tasks", "folder": "Tasks", "keywords": ["todo", "remind", "call back"] }
]
}
Use
# See where some text would be routed (no write):
voice-to-vault route --config config.json "remind me to call back the client"
# Ingest one capture (JSON arg or stdin):
voice-to-vault ingest --config config.json --json '{"id":"1","transcript":"..."}'
# Run the webhook listener (stdlib http server):
export VOICE_TO_VAULT_SECRET=your-shared-secret # optional HMAC-SHA256 verification
voice-to-vault serve --config config.json --port 8765
Point your provider's webhook at http://host:8765/. If VOICE_TO_VAULT_SECRET
is set, requests must carry an X-Signature: <hex hmac-sha256(body)> header.
Library
The pieces are usable directly, with no web layer:
from voice_to_vault import load_config, ingest, capture_from_payload
config = load_config("config.json")
result = ingest(config, capture_from_payload(my_payload_dict))
Tests
python -m unittest discover -s tests -t tests
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file voice_to_vault-0.1.0.tar.gz.
File metadata
- Download URL: voice_to_vault-0.1.0.tar.gz
- Upload date:
- Size: 14.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6062e9252ded26f7b90c4d1da795e40e503234e6fbc1f09f29c4f78d5bb580a0
|
|
| MD5 |
5efacf92b6278bced200166f65988119
|
|
| BLAKE2b-256 |
8198bec33567fcf1c39c0c7f0cc3bb0b510b89780c137d0351d2af13a8533246
|
File details
Details for the file voice_to_vault-0.1.0-py3-none-any.whl.
File metadata
- Download URL: voice_to_vault-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa81e8fa6df61a4f75713b109f600391e0311fd268329894d1a9ab087ead9b8b
|
|
| MD5 |
0c65a4d72b893c9ff4237a312d484128
|
|
| BLAKE2b-256 |
07628088ca42cfb63730f658e0bfadd70d9cbabb0aa15aab1ab01a762b35095c
|