v0.2.0 — multi-language, bring-your-own-AI, any CalDAV, mobile parity
Everything merged since v0.1.0.
No database migrations and no breaking changes — upgrading is a pull and a rebuild.
🌍 Otter speaks six languages
The booking page and Otter's UI are now localised in English, Spanish, French,
German, Portuguese and Italian. (#80, #92)
- Locale resolves stored preference →
Accept-Language→ default, and there's a
language picker in Settings → Preferences on both web and mobile. (#86) - Resolved on the server, so there's no hydration flash, and the marketing site stays
statically rendered. - Scope: the booking page and Otter's chrome are translated; the rest of the dashboard
is still English.
🤖 Bring your own AI provider
Otter is no longer tied to Claude. Set AI_PROVIDER and point it at whatever you run. (#83, #85)
anthropic(default) oropenai— whereopenaicovers any OpenAI-compatible
endpoint: OpenAI, Groq, OpenRouter, Together, Azure OpenAI, or a local
Ollama / LM Studio, viaOPENAI_BASE_URL.- Every AI feature works on either provider, including the streaming conversational
command bar (ported to OpenAI function-calling, not just the single-shot commands). - Models are overridable:
ANTHROPIC_MODEL_DEEP/FAST,OPENAI_MODEL_DEEP/FAST. - Defaults to
anthropic, so existing deployments need no changes.
📅 Connect any CalDAV server, not just iCloud
Apple iCloud was the only CalDAV option; now it's any server. (#82)
- Presets for Fastmail and mailbox.org, plus "Other CalDAV server" with a custom
URL — Nextcloud, Radicale, self-hosted, whatever you run. - Usernames no longer have to be email addresses (Nextcloud/Radicale use plain usernames).
- Security: a user-supplied server URL is now DNS-resolved and rejected if it points
at loopback, link-local, or private ranges — a regex check wasn't enough once arbitrary
URLs were allowed.
📱 The mobile app caught up with the web
Seven features that previously existed only on the web now have native screens. (#87)
- CRM connections, Billing/plan, Payouts (Connect balance + withdraw),
Packages & credits. - Team detail — member roster with roles and round-robin weights, add/remove members,
and collective rules. - Group polls — list, create with candidate times, see the per-option tally, finalize.
- Routing forms — list, create, rename, activate/deactivate, delete.
- Cancelling or rescheduling now captures an optional reason, sent to attendees.
New REST endpoints (added because the web read this data server-side, so no JSON
existed — useful to any API consumer, not just the app):
GET /api/integrations/crm · GET /api/payments/status · GET /api/teams/{id} +
DELETE /api/teams/{id}/members/{memberId} · GET /api/polls[/{id}] ·
GET /api/routing[/{id}]
Still web-only on purpose: the deep routing rule-builder and poll option editor render
read-only on mobile.
🔔 Android push is configurable per deployment
Android notifications go through Firebase Cloud Messaging, which needs your Firebase
project — so it's now pluggable rather than absent. (#93)
- Drop a
google-services.jsonintoapps/mobile/, or pointGOOGLE_SERVICES_JSONat
one (an EAS file secret). If it's absent the app still builds and runs — only
Android push stays off, so contributors aren't blocked. - Full runbook in
docs/INTEGRATIONS.md→ Mobile push (Android / FCM). - iOS push and the server side needed no changes; delivery already went through Expo.
🖥️ Website
- The interactive "Ask Otter" demo now sits directly below the hero instead of four
sections down, and replays the reschedule example on scroll. (#94)
Upgrade notes
- No migrations. No schema changes in this range.
- No config required.
AI_PROVIDERdefaults toanthropic; every new variable is
optional and commented out in.env.example. - Want a non-Claude provider? Set
AI_PROVIDER=openaiplusOPENAI_API_KEY(and
OPENAI_BASE_URLfor a self-hosted or third-party endpoint). - Want Android push? Follow the FCM runbook — it needs your own Firebase project.
Full changelog: v0.1.0...v0.2.0