Firsty BuildersGuidesprofileReference vs esimReference vs ICCID: which identifier when?
Integration

profileReference vs esimReference vs ICCID: which identifier when?

Three identifiers, three meanings, frequently confused. Here is which one to use where.

GTGauthier ThierensMay 19, 2026· 5 min read
Integration

profileReference vs esimReference vs ICCID: which identifier when?

When you provision an eSIM via the Firsty API, you get back multiple identifiers:

json
{
  "data": {
    "profileReference": "1234567890123456",
    "esimReference": "123456000000000001",
    "iccid": "89012345678901234567",
    "activationCode": "LPA:1$provider.example.com$ABC123"
  }
}

When you attach a package, you get another one:

packageReference
.

The most common question we get from new integrators: which one do I store? Which one do I pass back when I want to look up this SIM?

Here's the answer, plus the practical implications of using each.

What each identifier means

profileReference (16-digit numeric): identifies an eSIM profile slot. Think of this as the "account" the eSIM belongs to. One profile can hold multiple eSIMs across its lifecycle.

esimReference (18-digit opaque): identifies a specific eSIM instance within a profile. This is what you'll reference in most API calls.

ICCID (19-20 digit numeric): the SIM's globally unique serial number, defined by GSMA. Every SIM card in the world has an ICCID. It's the "MAC address" of a SIM.

packageReference (23-character opaque, e.g.

C123456XYZDUSR_A1B2C3D4
): identifies a specific package (a data plan instance) attached to an eSIM. An eSIM can have multiple packages over time.

planReference (14-character opaque, e.g.

C123456XYZDUSR
): identifies a plan from the catalog. A plan is the blueprint; a package is an instance of it.

The hierarchy: a profile contains eSIMs. Each eSIM has an ICCID. Each eSIM can have multiple packages over time. Each package is an instance of a plan from the catalog.

When to use each

Try it yourself

Free sandbox. Real Tier-1 carriers. 60 seconds from signup to credentials.

Get started →

For API calls about a specific eSIM: use

profileReference
+
esimReference
together.

javascript
await axios.get(`/profiles/${profileRef}/esims/${esimRef}`);

await axios.post(`/profiles/${profileRef}/esims/${esimRef}/packages`,
  { planReference: 'C123456XYZDUSR' });

Most operations on an eSIM need both. Store both.

For API calls about a specific package: use

packageReference
(plus the profile and eSIM references for routing).

javascript
await axios.get(`/profiles/${profileRef}/esims/${esimRef}/packages/${packageRef}`);

await axios.delete(`/profiles/${profileRef}/esims/${esimRef}/packages/${packageRef}`);

For displaying to the customer: use

iccid
.

Customers occasionally need to give their ICCID to support. It's the identifier that's also visible on their phone (Settings, General, About, ICCID). It's the universal identifier across the telecom world.

Don't show profileReference, esimReference, or packageReference to customers. These are internal to our API.

For your database primary keys: use your own UUIDs, not Firsty's identifiers.

sql
CREATE TABLE customer_sims (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id UUID REFERENCES customers(id),
  profile_reference TEXT NOT NULL,
  esim_reference TEXT NOT NULL,
  iccid TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Your UUID is your primary key. The Firsty identifiers are foreign keys to our system.

Treat identifiers as opaque

This is in our docs but worth repeating: treat all identifiers as opaque strings. Do not parse, decode, or make assumptions about their internal structure. The format may change without notice, and any code that depends on parsing identifiers will break.

For example, packageReference looks like

C123456XYZDUSR_A1B2C3D4
and there's a visible pattern. Don't write code that splits on the underscore and assumes meaning. Use the API to get metadata about a package, don't infer it from the reference.

Why so many identifiers?

You might reasonably ask why we have so many. The reason is:

  1. ICCID is a GSMA standard. Every SIM has one. We can't change this format.
  2. profileReference corresponds to a profile slot on the carrier side. One profile can have multiple eSIMs over time.
  3. esimReference identifies a specific eSIM within a profile.
  4. packageReference identifies a specific package instance. Each eSIM can have multiple packages.
  5. planReference identifies a plan in the catalog (the blueprint, not the instance).

In simpler eSIM APIs, you might see only ICCID. Those APIs don't support multiple packages per eSIM or eSIM replacement. Once an ICCID is provisioned with a plan, that's it.

In our model, you can suspend an eSIM, order new packages, terminate and replace. The customer's "account" (profile) persists across all of this.

Lookup patterns

Customer reports a problem with their eSIM: search by ICCID first. Customers can find this in their phone settings; you can find it in their order in your database.

sql
SELECT * FROM customer_sims WHERE iccid = $1;

From that row, you have the profileReference and esimReference for API calls.

Internal admin lookup: use your own UUID. Faster index, predictable format.

API operations on an eSIM: profileReference + esimReference together.

API operations on a package: packageReference (plus profile/esim references for routing).

Quick reference

IdentifierLengthFormatUse for
profileReference16 digitsNumericAPI calls about the profile
esimReference18 digitsOpaque numericAPI calls about a specific eSIM
ICCID19-20 digitsNumericDisplay to customer, support lookups
packageReference23 charsOpaque alphanumericAPI calls about a specific package
planReference14 charsOpaque alphanumericReference plans from the catalog

Store the first three when you provision an eSIM. Store packageReference when you attach a package. Treat them all as opaque. Reference them appropriately.

ShareLinkedInX
New guides every Tuesday. No marketing nonsense.