FAQ
September 2, 2026 · View on GitHub
What is ovos-opendata-server?
A FastAPI service that collects opt-in OVOS device metrics (wake-word audio, STT utterances, intent matches) into a database for open dataset building. See docs/index.md for the full documentation.
Is my voice recorded?
Only if you explicitly opt into wake-word (ww_urls) or STT (stt_urls)
uploads in your device's mycroft.conf. By default, nothing is uploaded at
all. If you only enable intent_urls, the server receives text (the
utterance and matched intent) and never any audio. See
docs/connecting-devices.md for exactly what
each upload contains, and privacy.md for the full policy.
Can I run my own server?
Yes — that's a first-class use case, not an afterthought. See
docs/self-hosting.md for a production Docker Compose
setup, or docs/getting-started.md to try it
locally first. Point your device's open_data config at your own instance
instead of (or in addition to) the public one, and your data never leaves
your network.
How do I run it?
cp .env.example .env
docker compose up --build -d
API on port 8007, dashboard at http://localhost:8007/. Full walkthrough:
docs/getting-started.md.
What does DATABASE_URL default to, and what should I use in production?
If unset, the server falls back to a local SQLite file
(sqlite:///./ovos_opendata.db) so it's importable and runnable with zero
configuration for local development and tests. Production deployments
should always set DATABASE_URL to a PostgreSQL DSN — SQLite doesn't
handle concurrent writers well and isn't a good fit once devices are actively
uploading. See docs/self-hosting.md.
Why do intake endpoints return 404 for unknown clients?
The User-Agent: ovos-metrics check returns 404 (not 403) intentionally,
to avoid making the write API discoverable to casual scanners or browsers
poking at the URL.
What's the GDPR / data-deletion story?
No personally identifiable information is collected by design — see privacy.md. Because uploads aren't tied to any account or verifiable identity, the server has no way to confirm who submitted a given record, so individual submitted records generally can't be deleted on request. If you need guaranteed control over your data, run your own server (see above) — you control deletion there directly, since it's your database.
How is language normalized?
All three record types lowercase and strip the language/lang value on
write (app/models.py), so "En-US" and "en-us " both end up stored and
queryable as "en-us".
What is the audio size limit?
Controlled by MAX_AUDIO_SIZE_MB (default 10). Exceeding it returns 413.
Are uploads validated?
Yes. Wake-word and STT audio must be a real WAV file (checked by its
RIFF/WAVE header) or the upload is rejected with 400. If the server has
API_KEY configured, uploads also need a matching X-API-Key header or they
get 401. All intake endpoints are rate limited per IP (RATE_LIMIT,
default 60/minute; exceeding it returns 429).
Does the list API include audio bytes?
No. Audio is excluded from /intents, /wake_words, and /utterances. Use
/wake_words/{id}/audio or /utterances/{id}/audio to stream individual
files.
How do I get the datasets?
GET /intents/export?format=csv (or ?format=json), and the same for
/wake_words/export and /utterances/export, with a hard cap of 100k rows
per request. See docs/datasets.md for column meanings and
notes on building training sets, and
docs/api-reference.md for every filter parameter.
How are dashboard stats cached?
In-memory, server-side, for DASHBOARD_CACHE_TTL seconds (default 60) — see
docs/dashboard.md.
How do I run tests?
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run pytest test/ -v --cov=app --cov-report=term-missing
See docs/development.md for the full contributor guide.