I picked up a new mobile line as an eSIM, and my carrier — congstar, on the Telekom network — had just moved every self-service function into its smartphone app. The website now shows invoices and nothing else. Which raised a stubborn little question: is the app actually required to get an eSIM onto a phone, or is it just where they decided to put the button? I did the obvious thing. I pulled the app and read it. Underneath the branding there is nothing proprietary at all — just a standard GSMA activation code and one line of code that assembles it.

The question

There are two completely separate things happening when you “activate an eSIM,” and conflating them is what makes the carrier app feel mandatory:

  1. Obtaining the credential — getting the activation code that points at your prepared profile. This is the part the carrier gates behind its app.
  2. Installing the profile onto the phone — the cryptographic download into the SIM chip. This is done entirely by the phone's operating system, and the carrier app never touches it.

The app lives entirely in the first box. Everything it does is deliver you a string. To see why that string is all that matters, you have to know what an eSIM is under the hood.

What an eSIM actually is

A plastic SIM is a tiny tamper-resistant computer — a smartcard — holding your network secrets: the ICCID (the SIM's serial number), the IMSI (your subscriber identity), and the Ki, a secret key that never leaves the card and is what actually proves to the network that you are you. An eSIM keeps every one of those identical. It changes the packaging and the delivery, nothing about the secrets themselves.

The chip is called an eUICC (embedded UICC): a GSMA-certified secure element soldered to the board, running a Java Card OS. It has one permanent identity of its own, the EID, and a certificate chain rooted in the GSMA Certificate Issuer — which is how a remote server can be sure it's talking to a genuine, non-cloneable SIM chip and not somebody's emulator. Where a plastic SIM holds one identity, an eUICC can store several profiles and keep one enabled at a time (an iPhone 16e supports dual eSIM — two enabled at once). A “profile” is simply one digital SIM: ICCID + IMSI + Ki + operator settings, delivered as data instead of as a card.

Remote SIM Provisioning

How a profile gets from the carrier into your chip is a GSMA standard called Remote SIM Provisioning — spec SGP.22, the consumer variant. Three players:

  • SM-DP+ — the carrier's profile server (here, a Telekom server). It prepares and encrypts your profile and waits for a device to come collect it. Its address is a plain hostname.
  • LPA (Local Profile Assistant) — software built into the phone's OS (iOS, Android). It is the thing that talks to the SM-DP+ and drives the download. This is the part the carrier app is not.
  • SM-DS (Discovery Server) — optional; the mechanism behind “your eSIM sets itself up during device setup” with no QR at all.

The activation code you scan from a QR is just a pointer with a fixed shape: LPA:1$<SM-DP+ address>$<matching ID> — meaning “go to this server, and ask for the profile filed under this matching ID.” When the LPA connects, the eUICC and the SM-DP+ mutually authenticate via those GSMA-rooted certificates, the server binds the profile to your specific EID, and streams down an encrypted Bound Profile Package that only your chip can decrypt. Then the chip installs and enables it.

Decompiling the app

The congstar app is a normal Play Store download, so no device is needed to get it. apkeep pulls the package, and jadx turns its Dalvik bytecode back into readable Java/Kotlin. The version I read was 6.14.1.

# pull the app straight from the store, no phone involved
apkeep -a de.congstar.meincongstar .        # -> de.congstar.meincongstar.xapk  (v6.14.1)
unzip -o de.congstar.meincongstar.xapk -d xapk

# decompile the base APK to Java source
jadx -d out xapk/de.congstar.meincongstar.apk

# then just grep for the eSIM machinery
grep -rniE 'LPA:1\$|EuiccManager|smdpAddress|activationCode' out/sources

The interesting terms surface immediately: a data model with smdpAddress and activationCode fields, a screen for manual entry of exactly those two values, a copy-to-clipboard handler for the activation code, and a reference to Android's system eSIM class. None of that is proprietary; all of it is the vocabulary of SGP.22.

What the bytecode shows

The app fetches one JSON object from the carrier backend — internally an ESimDto — and its fields are just the standard eSIM primitives:

// returned by GET  app.congstar.de/congstar-rest-service/app/v13/customers/{id}/contracts/{id}/esim
ESimDto {
  activationCode    // the matching ID  (the per-profile one-time secret)
  smdpAddress       // the SM-DP+ server that is holding your profile
  qrCode            // optional: a pre-rendered QR image (base64 PNG)
  activationUrl     // the LPA:1$...  universal link
  status, eSIMSupported, legitimation, cscUrl, links, ...
}

And here is the entire “secret sauce” of how the app produces a scannable eSIM — the one line, in the code path for showing a QR to another device. It concatenates a string:

// smdpAddress and matchingId come straight from the ESimDto above
sb.append("LPA:1$" + smdpAddress + "$" + matchingId);

That's it. That string is the eSIM QR. The app either draws it as a QR code, shows the backend's pre-rendered qrCode bitmap, or presents it on a manual-entry screen as two copyable text fields. There is no cryptography, no device binding, no carrier magic in the app — all of that lives in the SM-DP+ server and the phone's chip.

What about installing “on this device”? On Android the app touches the system eSIM class exactly once — to ask whether the phone supports eSIM at all:

Object systemService = application.getSystemService("euicc");
EuiccManager euiccManager = systemService instanceof EuiccManager
        ? (EuiccManager) systemService : null;

// ...used only to set a capability flag:
boolean eSIMSupported = euiccManager != null && euiccManager.isEnabled();

// there is no downloadSubscription() / forActivationCode() anywhere in the app.

No downloadSubscription(), no forActivationCode() — the app does not even perform the install itself on Android. It defers to the OS. Around all of this sits the plumbing you'd expect: login via OpenID/Keycloak at sso.congstar.de, and an SMS one-time code (requestConfirmationCode / validateConfirmationCode) that gates the request. That OTP is the carrier's own security check — it is not part of the eSIM standard.

Step in activating your eSIMWho actually does it
Log you inThe app — OpenID/Keycloak at sso.congstar.de
Gate the request behind an SMS codeThe app + carrier backend (requestConfirmationCode)
Prepare & encrypt the profileThe carrier's SM-DP+ server (Telekom)
Hand over the activation codeThe app — as a QR, or plain text you can copy
Download & install the profileThe phone OS's built-in LPA (iOS itself; on Android, EuiccManager)
Enable / switch / delete profilesThe phone OS
The carrier app owns exactly one row — delivery. Every step that touches the actual SIM credential happens in a GSMA-certified server or in the phone's own operating system.

So, is the app required?

For the eSIM standard: no. The app is a delivery vehicle for a bog-standard activation code. What it hands you — an SM-DP+ address and a matching ID — is exactly what any eSIM QR encodes, and once you have it, the app has no further role. The install is done by the phone. The honest caveat is that the carrier has chosen to expose that code only through the app (the old “activate without the app” web page looks abandoned), so in practice the app is the gatekeeper that reveals the credential. But the credential, and the download, are pure GSMA. You need the app once, on any phone, to see the code — not to use it.

Activating on an iPhone without the app touching it

Because the credential is a standard LPA:1$smdp$matchingId, and the app literally shows you the SM-DP+ address and activation code as copyable text, the iPhone never needs the carrier app at all:

  1. Open the carrier app on any phone (or the old one), go to the eSIM screen, and choose the “activate on another device” path. It shows a QR — and, behind it, the SM-DP+ address and activation code in plain text.
  2. On the iPhone: Settings → Mobile Data → Add eSIM → Use QR Code, and scan it. iOS's own LPA contacts the SM-DP+ and downloads the profile.
  3. Prefer no camera? Tap Enter Details Manually and type the two values — SM-DP+ address and activation code — straight into iOS. Same result, zero app on the iPhone.

Field report: the app's punchline

I did all of this on my own new line, and the eSIM installed in seconds — exactly as the standard promises. The comedy was getting into the app to trigger it. The carrier has no web path left, so you must log in to the app — and the login demanded an SMS two-factor code, sent to a number I could only reach on my old phone, for a new number that didn't exist yet. To activate the eSIM that would give me a working phone, I had to keep the old phone in hand to receive the code.

The takeaway

An eSIM isn't the app, and it isn't even really the QR code. It's a network-secret profile that a GSMA-certified server streams, encrypted and bound to one chip, into your phone — orchestrated start to finish by the phone's own operating system. The carrier app is the clerk who reads you the ticket number.

That's worth internalizing beyond this one carrier. The next time an operator tells you their app is the only way to do something with your line, it's usually worth asking what standard sits underneath — because on eSIM, the standard is doing all the real work, and the app is a thin coat of paint over a two-field string.

Methodology

This was interoperability research on my own contract and my own device: a legally purchased app, decompiled locally to understand how a line I pay for is provisioned. Tools were apkeep (store download) and jadx (Dalvik → Java). Nothing was sent anywhere, no carrier system was probed or attacked, and no account identifiers appear here — the findings are entirely about the mechanism. Obfuscated class and field names from the decompiler have been lightly renamed for readability; the LPA:1$ concatenation, the ESimDto fields, the EuiccManager capability check, and the absence of any downloadSubscription() call are all verbatim from the bytecode of version 6.14.1.