Telecom
Telz: international calls with the price up front
Since 2023 we have been the Android team behind Telz, an app for low-cost international calls. We turned an open-source SIP client into a paid product with upfront pricing and top-ups.
Project facts
- Client
- Telz (Nettia, Finland)
- Year
- 2026
- Timeline
- Since 2023, ongoing
- Platforms
- Android
- Stack
- Kotlin, VoIP / SIP, Compose
- 3+ years Continuous partnership — Features, platform updates and support since 2023
- 54 Releases in the changelog — Shipped with the client's in-house lead, 2023 to 2026
- 36 Locales — Including right-to-left Arabic, Hebrew and Persian
What made the difference
- Per-minute rate and call type shown before every international call
- Linphone fork moved to Hilt, MVI view models and Compose, layer by layer
- Real-device UI tests on Kobiton and translation checks for 36 locales
Telz has been selling international calls since 2012. Its Android app started life as a fork of Linphone, the open-source SIP client: a dependable calling engine inside an interface built for a generic softphone rather than for someone calling family in another country. Since 2023 our team has worked on the app next to the client's in-house lead, shipping features, keeping up with each Android release and moving the codebase to modern Kotlin without stopping the roadmap.
The brief in one line: turn a generic softphone into a calling product people pay for, keep releasing, and leave the proven SIP core alone.
Where we started
The Linphone base gave Telz reliable VoIP, but everything around the call was generic. The first minutes in the app said little about price, privacy or how top-ups work. The code mixed several generations of Android patterns, so even small changes took longer than they should.
What we built
The price before the call
When a user taps a contact, Telz resolves the number to a country and lays out the options: an internet call over Wi-Fi or mobile data, or a callback in which Telz rings both people. Each option shows its per-minute rate, and we added a line that tells how many minutes the current balance covers. The choice takes a second, and the bill holds no surprises.
An onboarding that answers the first questions
The new onboarding is written in Jetpack Compose. Three short screens cover what a first-time user wants to know: no registration beyond a phone number, top-ups in any currency, and call recordings that stay on the device. A rates sheet with a currency switcher is one tap away, and a short data-privacy note appears before sign-in.
Payments and growth
We hardened the Google Play purchase flow against failed product loads and empty catalogues, built payment settings with auto top-up, and added an invite-a-friend bonus with promo codes. Install referrers and validated purchases go to AppsFlyer, so marketing can see which channels bring paying users.
Keeping pace with Android
Every Android release touches something a calling app relies on. We moved the app to edge-to-edge layouts for Android 15, prepared it for 16 KB memory pages, moved Linphone work onto its own core thread, added native in-app updates and a network observer, and worked through crash and ANR reports one by one.
Modernising without a rewrite
A full rewrite would have frozen the roadmap for months. We replaced the foundations one layer at a time, so each release still carried product work.
| Area | What we did | Why it matters |
|---|---|---|
| Dependency injection | Hilt across the app and the Linphone integration | One home for singletons, testable components |
| Presentation | MVI-style view models with state, events and one-off effects | Predictable screens, no lost navigation events |
| New UI | Jetpack Compose inside the existing fragment navigation | New screens ship without touching old ones |
| Networking | Coroutines and a typed result wrapper, legacy RxJava calls kept | Endpoints migrate one at a time |
| Phone numbers | Own country picker and validator on libphonenumber | Correct E.164 numbers mean correct rates |
| Quality | Robot Framework and Appium tests on real Kobiton devices via GitHub Actions | Regressions show up on real hardware before release |
One app, 36 locales
Telz users call across borders, so the interface speaks their languages, including Arabic, Hebrew and Persian, which read right to left. A translation check flags missing and inconsistent strings before a release goes out.
What the client has today
- A calling flow where the price and the call type are clear before the first ring
- A codebase where new screens are built in Compose on a consistent architecture
- Releases that follow Android platform changes instead of chasing them
- An automated regression suite that runs on real devices
Running an Android product on an open-source core that needs to grow into something customers pay for? We would be glad to look at it with you.