eSIM Provisioning Integration
What an MVNO or a connectivity platform has to build to move a profile from an SM-DP+ into a subscriber's eUICC: the consumer architecture and the components that own each step, an activation code whose positional fields fail quietly, two client paths that decompose that code differently and the authorization each platform demands before your app may download anything. With the failure we expect named at every step.
- Two documents cover consumer eSIM and the version numbers mislead SGP.21 is the architecture and SGP.22 the technical specification, and SGP.22 runs on two parallel lines where the higher number is the older document.
- The activation code is positional, so empty fields must be emitted A generator that skips optional fields rather than leaving their positions empty produces a code that fails on the handset and not on the server.
- Carrier privileges arrive in the profile metadata A carrier app is authorized by the operator through the subscription itself, and an app without the system permission gets a user-consent prompt in the middle of the download instead of a clean failure.
- Key subscriber records on neither the EID nor the ICCID alone The chip identifier outlives every profile and the profile identifier dies with it, so carry your own subscription key and the matching identifier beside both.
- Accreditation has a date and interoperability has a test specification Ask an SM-DP+ operator for a current certificate rather than a claim, and treat the server and chip pairings your subscribers will hit as the test matrix.
eSIM provisioning integration moves a subscription out of a server you control and into a chip you do not. An MVNO or a connectivity platform orders a profile from an SM-DP+, hands the subscriber something to scan or tap and then has to establish that the profile reached the eUICC, installed, enabled and reported back. The GSMA states the scope of the standard in one line on its consumer eSIM page: "eSIM is a global specification by the GSMA which enables remote SIM provisioning of any mobile device." Which parts of that chain your platform owns is not something the specification settles.
In short: a back-end job and a client job that fail in different places. The back end orders, confirms and releases a profile at the SM-DP+ and builds an activation code whose positional fields break quietly. On the device an app either hands off to the platform's own eSIM flow or drives the download itself, which turns on carrier privileges carried in the profile metadata on Android and an entitlement on iOS. Two failures cost the most time: on Android a download without the system permission demotes silently to a consent prompt instead of failing, and the iOS path wants that same activation code split into named properties where Android takes the string whole. Below, the step table names what fails where.
Where an eSIM profile comes from
Two GSMA documents cover consumer remote SIM provisioning and teams routinely cite the wrong one. The architecture and requirements document is SGP.21, whose landing page states the scope: "This document describes the architecture and technical requirements for Remote SIM Provisioning of all device types across all consumer markets."
The specification an implementation is built against is SGP.22. Its V3.1 page describes a document that "provides a technical description of the GSMA's 'Remote SIM Provisioning (RSP) Architecture for consumer Devices', SGP.21 v3.1 that applies for eSIM Products."
Version numbering is a trap. SGP.22 is maintained on two lines at once, and V3.1 is dated 1 December 2023. The v2.6.1 page describes the same thing in almost the same words: "This document provides a technical description of the GSMA's 'Remote Sim Provisioning(RSP)' Architecture for consumer Devices." It carries a publication date of 25 April 2025, nearly seventeen months later. The higher version number is the older document. Never derive a date from a version number.
The runtime cast is small. An eUICC is a chip. An SM-DP+ prepares a profile, stores it and delivers it. On the handset an LPA drives the download and talks to both. Android's documentation for the eSIM stack describes the LPA as that bridge, optionally carrying a user-facing component it calls the LUI, and notes that the framework discovers and connects to the best available LPA itself.
One constraint shapes every product decision downstream, stated on the GSMA's own eSIM page: "eSIM allows consumers to store multiple operator profiles on a device simultaneously, and switch between them remotely, though only one can be used at a time." A platform that models a subscriber as holding several live subscriptions on one device has modeled something the hardware does not do. Our telecom software development guide covers the OSS and BSS systems this chain hangs off.
Provisioning steps and what fails at each one
Each row stands on its own. The step, actor and interface columns are sourced, with interface names taken from the SGP.22 v3.1 section headings and from the platform documentation cited below. The what-fails column is Pharos Production practice wherever it does not name a platform constant or a source already cited above, because none of the pages read for this article tells an MVNO which failure to expect at which step. A sourced cell names the specification section or the platform constant it rests on; a cell opening with the word Practice is ours.
| Step | Actor | Interface (sourced) | What fails (our practice unless a constant or a cited source is named) |
|---|---|---|---|
| Order a profile from the SM-DP+ | Platform back end | ES2+ DownloadOrder, SGP.22 section 5.3.1 |
Practice: the order succeeds, the MatchingID is not retained and no activation code can be built |
| Confirm and release the profile | Platform back end | ES2+ ConfirmOrder and ReleaseProfile, sections 5.3.2 and 5.3.4; CancelOrder aborts |
Practice: a profile confirmed and never released is invisible to the device, so the subscriber holds a code resolving to nothing |
| Build the activation code | Platform back end | SGP.22 section 4.1, Table 8 | Optional fields omitted rather than emitted as empty positions; a format value other than 1, treated as invalid |
| Deliver the code to the subscriber | Platform, then subscriber | QR code, in-app hand-off or manual entry | The LPA checks whether the caller's package name is allowed, so an unlisted package gets no flow at all |
| Parse the code on the device | LPA, or the carrier app | Android DownloadableSubscription.forActivationCode; iOS CTCellularPlanProvisioningRequest properties |
Android takes the string whole while iOS needs it split into named fields, so a QR-only platform has no iOS path |
| Authorize the calling app | Carrier app, LPA | Android carrier privileges in the profile metadata; iOS CoreTelephony entitlement | ERROR_INVALID_ACTIVATION_CODE, or a silent demotion to the consent path where EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR prompts the user |
| Authenticate the device to the SM-DP+ | LPA and eUICC | ES9+ InitiateAuthentication and AuthenticateClient, sections 5.6.1 and 5.6.3 |
ERROR_CERTIFICATE_ERROR and ERROR_CONNECTION_ERROR. TLS is mandated, so a middlebox reads as a broken profile |
| Present the profile to the subscriber | LPA | Profile Metadata returned in the ES9+.AuthenticateClient response |
The metadata carries the operator name on the confirmation screen, so a misconfigured profile looks like another carrier |
| Confirm with a confirmation code | Subscriber and SM-DP+ | SGP.22 section 4.7; Android getConfirmationCode; iOS confirmationCode |
ERROR_INVALID_CONFIRMATION_CODE. Shipping it beside the activation code removes the reason for having one |
| Download and install the profile | LPA and eUICC | ES9+ GetBoundProfilePackage; ES10b LoadBoundProfilePackage; Android downloadSubscription |
ERROR_EUICC_INSUFFICIENT_MEMORY, ERROR_INSTALL_PROFILE, ERROR_DISALLOWED_BY_PPR, ERROR_TIME_OUT |
| Enable the profile | LPA, eUICC and the user | Android switchToSubscription, or the platform settings screen |
Only one profile runs at a time, so enabling the new one disables the old |
| Report the outcome to the server | LPA | ES9+ and ES2+ HandleNotification, sections 5.6.4 and 5.3.5 |
Server state diverges silently. On Android a GSMA-side failure arrives as a SubjectCode and ReasonCode in a detailed-code extra |
| Delete the profile | Carrier app or user | Android deleteSubscription, or the settings screen |
Deleting the enabled profile leaves the handset with nothing active |
| Test the whole chain | Platform team | The SGP.23 test specification; an SM-DP+ operator holding SAS-SM accreditation | Interoperability faults between SM-DP+ and eUICC are what the test specification exists to reduce |
Three of those rows cost more than the rest in our practice, and for the same reason each time: the failure lands where nobody is watching. A profile ordered and never released leaves a server record that looks complete and a subscriber whose code resolves to nothing, so the ticket arrives as a client bug and gets triaged against the app. An error constant with no mapped message reaches support as an opaque string, and each instance becomes a conversation instead of a self-service answer. A code redeemed once and then reissued sends the subscriber back through a flow that cannot succeed.
The activation code and its entry points
Everything the device needs travels in one string. SGP.22 v3.1 defines the activation code in section 4.1, Table 8, as an ordered concatenation separated by the $ delimiter. Three parts are mandatory: a format field that shall be set to 1, the fully qualified domain name of the SM-DP+ and the AC_Token, which is the MatchingID. Three optional parts follow in fixed positions: the SM-DP+ object identifier from its authentication certificate, a flag set to 1 when a confirmation code is required and an eSIM CA root public key indicator. One further field exists only in a code transferred between devices.
Two consequences of that layout cost real engineering time. Because the fields are positional, a back end must emit empty fields rather than skip them, which means two consecutive delimiters where a value is absent. And because the format field is fixed, a device treats any other value as invalid and ignores parameters beyond those defined, so a hand-rolled generator fails silently on the handset rather than loudly on the server.
Maximum length: 255 characters, unless the code carries the device-change field, in which case it may run longer.
That same string reaches the subscriber by three routes, and a platform supporting only one has left subscribers without a path. A QR code is what everyone builds first. Manual entry is the fallback and the one that produces transcription failures. In-app provisioning skips code presentation entirely, handing the string straight to the platform flow.
The confirmation code belongs to a different actor than most teams assume. The GSMA describes it in its response to an independent security analysis: "In addition to the Activation Code, the MNO can provide the Confirmation Code to the subscriber that will be verified by the SM-DP+ during the Profile download." Verification happens at the server, not on the device. That is why delivering both codes in one email or one QR payload defeats the mechanism: the second factor is only a factor while it travels separately.
The server side of an eSIM integration
SGP.22 organizes its functions by named interface, and using the specification's names rather than invented ones is the cheapest interoperability decision available. ES2+ is the operator to SM-DP+ interface, ES6 the operator to eUICC, ES8+ the SM-DP+ to eUICC, ES9+ the LPA to SM-DP+ and the ES10 family the LPA to eUICC. Your back end lives almost entirely on ES2+, and the rest of that list is somebody else's implementation you integrate against.
The functions your platform calls on ES2+ are named in the section headings of SGP.22 v3.1 section 5.3: DownloadOrder, ConfirmOrder, CancelOrder, ReleaseProfile, HandleNotification, RpmOrder and HandleDeviceChangeRequest. That is the function set of the interface and the order in which the specification presents them, not a state machine this page is in a position to assert.
Our practice is to treat the MatchingID as a first-class record rather than a field inside a generated string. It ties a subscriber, an order and an eventual notification together, and must survive independently of the activation code, because a code can be regenerated from stored parts while a lost MatchingID means a new order. Keep the SM-DP+ address beside it, since a platform with two SM-DP+ relationships cannot tell which server holds the profile.
One adjacent chain is worth naming and then leaving alone.
An entitlement server per GSMA TS.43 sits alongside remote SIM provisioning. It is a separate configuration surface with its own failure modes.
The client side on Android

The failure that costs the most time on Android looks like success. Downloading a subscription requires either a system permission a carrier app does not normally hold, or that the app be authorized, per the subscription metadata, to manage both the currently active subscription and the one being downloaded. Without the former, the documentation says the call returns a resolvable error in the callback intent so the user can be prompted to accept. That is not an error in any ordinary sense: it is a working flow with a consent screen in the middle, and a team measuring completion rates sees the drop without the cause. Everything below decides which of those two authorizations your app can actually hold.
Android exposes eSIM management through a framework API rather than direct chip access, so the question reduces to what your app is allowed to call. The EuiccManager reference documents the entry point carrier apps use, and the platform's eUICC API guide draws the line that matters: an LPA app has to be a system app included in the system image to call the system API, while a carrier app needs carrier privileges granted by eUICC profiles instead. The eUICC API guide also lists SM-DS support as an optional LPA capability for discovering further downloadable profiles. That distinction decides whether an MVNO builds an app or builds an LPA. In our practice it is almost always an app.
Carrier privileges come from the carrier, not from a platform request. Android's documentation describes them as added by the mobile carrier in the profile metadata, so authorization for your app is provisioned along the same path as the subscription. That mechanism is documented on the UICC carrier privileges page: certificates on the card grant API access to apps signed by them, with an optional package reference that narrows the grant so both must match. Distribution is the stated design goal, letting an operator ship through a public store instead of preinstalling. Our mobile app development guide covers that release side.
There are two shapes for the download. In the hand-off shape the carrier app launches the platform's activation flow with an intent action and lets the LUI drive, optionally asking it to open a QR scanner; the reference notes that the LPA checks the caller's package name. In the direct shape the app builds a downloadable subscription from the code and calls the download itself. The reference for DownloadableSubscription describes that object being created from an activation code parsed from a QR code, with a server address parsed out of the code to fetch further profile information such as the carrier name and access rules.
Deletion carries the mirror of that permission rule. The reference for deleteSubscription requires the caller to hold carrier privileges according to the metadata of the profile being deleted, or the same embedded-subscriptions permission that gates the download, so a carrier app can only remove the profiles whose metadata grants it privileges and a platform cannot clean up another operator's profile from its own app.
One version note rather than a version story. The framework eSIM APIs arrived with Android 9 and the class is marked as added in API level 28, the activation intent path is API level 30, multiple eSIMs need Android 10 or higher and carrier privileges go back to Android 5.1. Name the release that gates the capability you use. The reference also pins its GSMA citations to SGP.22 v2.2 section numbers, so Android implementing a current SGP.22 line is not a claim the documentation supports.
The client side on iOS
Apple's CoreTelephony documentation covers the same job with a much smaller surface. CTCellularPlanProvisioning is documented as the object a carrier app uses to download and install a carrier eSIM, with methods to start provisioning for a specified plan and to ask whether the device and the app meet the requirements, plus a property indicating hardware eSIM support.
The request object matters more than the class, and it is why a platform cannot treat iOS as a second QR target. Where Android accepts the encoded activation code as one string, CTCellularPlanProvisioningRequest is documented as a set of named properties: an address for the carrier's eSIM server, which is the one that must be set, plus optional confirmation code, EID, ICCID, matching identifier and object identifier. Your activation code has to be decomposed into those fields before the call. A back end that stores only the assembled string, never the parts, has an Android path and no iOS path, and discovers this late.
Access is gated by an entitlement rather than by profile metadata. Apple's documentation states that the class is available only to carrier apps holding a suitable entitlement, and names com.apple.CommCenter.fine-grained as the entitlement granting access to CoreTelephony services. Requesting it is not a build-configuration change, and in our experience it belongs on the project plan beside the SM-DP+ contract.
Profile lifecycle and the identifiers you key on
Treat enable, disable and delete as operations rather than as a state diagram, because operations are what the platform APIs expose. On Android the platform settings screen lists embedded profiles and lets the user switch between them, download new ones and delete unused ones, so a subscriber can change the state of your subscription without touching your app. An integration that treats its own UI as the only mutation path will drift.
Enabling is the operation that surprises people, because only one profile can be used at a time. Enabling a newly downloaded profile disables whatever was enabled before, and where that was the subscriber's primary line it is a visible event.
Which identifier to key on is engineering practice and nothing on this page sources it, so read it as ours. The EID identifies the chip and survives every profile that passes through it. The ICCID identifies the profile and dies with it. Keying on the ICCID means a device change rewrites your primary key; keying on the EID means a subscriber who replaces the handset looks new. We key on our own subscription identifier, carry the MatchingID beside it as the link to the SM-DP+ order and store both as observed attributes.
Notifications close the loop and are the part most often deferred. Profile policy rules, when an operator declares them in the profile metadata, are handled by the LPA rather than by the platform framework, so a rule that blocks a deletion produces a refusal your server learns about only through the notification handler. Treat the notification handler as part of the first release, not as telemetry.
Security and what the specification leaves to you
The protocol has been analyzed independently, and on the headline question the GSMA writes: "The GSMA notes and welcomes the findings of the researchers that the remote SIM provisioning (RSP) protocol is adequately secured, between honest entities, against network adversaries and the security goals defined in the eSIM specifications are achieved."
The qualifier carries the weight, and the same response gives the threat model behind the published attacks: "The potential attack scenarios, and the vulnerabilities that could arise from those, that are described in the research paper assume that one of the endpoints (the SM-DP+ and eUICC) is compromised." That assumption of honest endpoints makes choosing your SM-DP+ operator a security control rather than a procurement detail.
Transport security is not optional, and on the same page the GSMA is explicit that "In the case of the GSMA's remote SIM provisioning protocol, the use of TLS is mandated and if the integrity of its implementation is maintained the exposure to the vulnerabilities described in the research paper is minimised." For an integration team that becomes a concrete rule in our practice: treat any middlebox or debugging interception between the LPA and the SM-DP+ as a functional break, and expect it to present as a certificate or connection error on the handset rather than as anything a server log explains.
Two obligations sit outside the protocol. First is identity, where the GSMA writes that "When the user requests a profile for their eUICC, the MNO should verify the user identity carefully." Nothing in SGP.22 does that for you, and an activation code is a bearer token in every practical sense.
Second is the flow itself, and the GSMA response states that boundary plainly: "The user experience is outside the scope of the GSMA eSIM specifications because the detailed user experience is device implementation specific." Every abandoned activation and every ticket about a profile that downloaded and never enabled lives on your side of it.
Testing before a subscriber ever sees it
Interoperability is the failure class this ecosystem was built to manage, and the GSMA names its instrument in the same response: "GSMA Test specification (SGP.23) plays a vital role in ensuring interoperability within the eSIM ecosystem, significantly reducing functional errors during communication between the SM-DP+ and the eUICC." The reading for a platform team is that the pairings your subscribers actually hit are the test matrix,.
Accreditation is the other half, and the question to ask an SM-DP+ operator before the contract. The GSMA's Security Accreditation Scheme page describes a program that "enables mobile operators, regardless of their resources or experience, to assess the security of their UICC and eUICC suppliers, and of their eUICC subscription management service providers."
Two schemes sit under it, covering different suppliers. SAS-UP is described on the same page as the scheme through which "UICC and eUICC manufacturers subject their production sites and processes to a comprehensive security audit." The one that applies to an SM-DP+ operator is the other: "To ensure industry confidence in the security of remote provisioning for eUICCs, a related security auditing and accreditation scheme exists for the providers of eUICC subscription management services." Accreditation is not permanent either, since "Successful sites are awarded security accreditation for a period of one year, extending to two further years upon each successful renewal." Ask for the current certificate and its date, not for the fact that one existed.
Our own pre-launch checklist is practice rather than a conformance test, and passing it certifies nothing. Every entry point is exercised on real hardware from both platform families, which in our practice means Android handsets from more than one manufacturer at more than one API level alongside current iPhones. A code is redeemed twice, and whatever the second attempt returns is mapped to a subscriber-facing message. Downloads are interrupted mid-flight and resumed. A profile is deleted while enabled and the recovery path is watched by a human. Every error constant the client can receive has a mapped subscriber-facing message, because an unmapped code becomes a support call.
eSIM in constrained IoT devices
The consumer architecture assumes a user with a screen who can consent, which is what an IoT fleet does not have. The GSMA puts the reason for a separate specification in one sentence on its eSIM page: "The eSIM IoT specification is designed to be used in IoT devices that are network-constrained or user interface constrained."
The SGP.32 page scopes its own document as covering "The remote provisioning and management of the eUICC in IoT Network Constrained and/or User Interface Constrained Devices."
Read the two sentences together and the scope boundary is simple. If a person can be put in front of the device to accept a profile, the consumer chain this article describes is the right one. If no person can be, the IoT specification applies instead, with its own remote-management component in place of the consumer LPA, and a consumer integration does not carry over unchanged. Older machine-to-machine estates are a third family rather than an early draft of either, covered by the GSMA's M2M page, so a platform serving both consumer handsets and legacy modules integrates against two server-side models.
Three situations put a platform outside this guidance altogether. Devices that are network constrained or have no user interface belong to the IoT specification above, because nothing in the consumer flow enables a profile without a person. An established operator with an existing subscription-management estate is answering a question about its own systems rather than building a greenfield chain. And a device maker shipping hardware rather than a service sits on the LPA path, which needs a system app in the system image, so none of the carrier-app advice here transfers.
How Pharos Production helps
An eSIM integration is a back end that orders and tracks profiles, an activation-code generator whose positional fields are right, two client paths that decompose that code differently, a notification handler that keeps server state honest and an authorization path on each platform.
Our telecom software development practice builds those components, from the SM-DP+ integration and its order records to the failure-mode mapping that turns error constants into something support can act on.
Sources: GSMA pages for SGP.21 V3.1, SGP.22 V3.1, SGP.22 v2.6.1, SGP.32 v1.3, eSIM, consumer eSIM, eSIM for M2M, the Security Accreditation Scheme and the GSMA response to the consumer RSP security analysis; the SGP.22 v3.1 PDF, paraphrased by section; Android Open Source Project eSIM and UICC documentation with the EuiccManager and DownloadableSubscription references; Apple developer documentation for CTCellularPlanProvisioning and CTCellularPlanProvisioningRequest, cited by title. Read on 17 September 2026. Engineering guidance, not legal advice.
FAQ
Quick answers to common questions about custom software development, pricing, process and technology.
Type to filter questions and answers. Use Topic to narrow the list.
Showing all 6
No matches
Try a different keyword, change the topic or clear filters
-
In our practice, buy it, and the reason is accreditation rather than engineering effort. An SM-DP+ operator falls under the related GSMA accreditation scheme for providers of eUICC subscription management services, and accreditation there runs for a year and renews rather than being awarded once.
Building the server means absorbing that program permanently alongside the profile-preparation machinery and the certificate estate behind it. The parts genuinely worth building in-house are the order records, the activation-code generator and the notification handling, because those carry your product logic and nobody else can key them the way your subscriber model does.
-
Our practice is to choose by what the subscriber is holding at the moment of purchase. A QR code is the default where buying happens on a second screen or in a shop, since the handset that scans it is the one receiving the profile.
In-app hand-off wins where the subscriber already has your app and an account, because the code never becomes a string a human reads. Manual entry is the fallback, not a primary path.
-
That depends on the SM-DP+ and on how your platform issued the code, which is why it belongs in your test plan rather than in your assumptions. A code is tied to a specific prepared profile through the matching identifier, and what an SM-DP+ does with a second request against that identifier is the operator's implementation choice.
What varies is the error the handset shows, and in our testing it is seldom one that tells the subscriber the code was already used. Our practice is to redeem a code twice on real hardware before launch and to map whatever comes back to a message support can act on.
-
Not directly. Both platform paths run through native APIs gated on the app itself: carrier privileges inside the profile metadata on one side, an entitlement issued to a carrier app on the other.
A web flow can do everything up to the hand-off, including the order to the SM-DP+ and presentation of the activation code. The install itself happens in software the operating system trusts.
-
At project start, as a scheduled dependency rather than a task inside the sprint that needs it. Apple's documentation makes the provisioning class available only to a carrier app holding a suitable entitlement, so the iOS path does not work at all until the request is granted, and no amount of build configuration substitutes for it.
Our practice is to open the request as soon as the carrier relationship is real, build the iOS path against the request object's named fields while it is pending and keep a QR fallback in the product.
-
Five things, and in our experience asking for them late is where the schedule slips. The current accreditation certificate with its date, since accreditation runs for a year and renews.
Test credentials against a non-production environment, plus at least one test profile per device family you intend to support. The exact interface and version the operator exposes on the operator-facing side, because that is what your back end integrates with. The notification path and whether it reaches you by callback or by polling. And a written answer on how profiles are released, since a profile ordered and never released is the failure that looks like a client bug.
I work with startup founders who need a dedicated software development team but don’t want to gamble on hiring, random outsourcing, or opaque delivery.
Most founders face the same problem sooner or later.
Early technical and team decisions lock the product into tech debt, slow delivery, missed milestones and constant re-hiring. By the time this becomes visible, fixing it is already expensive.As a CTO and software architect, I help founders design, build and run dedicated development teams that work as a true extension of the startup. Not as a black-box vendor.
My focus is on complex products where mistakes are costly:
- Web3 and blockchain platforms
- FinTech and regulated products
- High-load startup systems
- MVP → scale transitions
We don’t do body-shopping.
We don’t sell generic outsourcing.Instead, we help founders:
- build the right team structure from day one
- keep technical ownership and transparency
- scale delivery without losing control
- avoid vendor lock-in and hidden risks
Teams are aligned with the product roadmap, business goals and long-term architecture. Not just short-term velocity.