Implement Evergreen Roaming
Evergreen Roaming gives every user a permanent eSIM they top up with data packages on demand. Your backend provisions the user once, and Gigs Connect handles installation, checkout, account management, and support from then on.
Prerequisites
Before you begin, you need:
- A Gigs project with Gigs Connect and Gigs Billing enabled
- Your Gigs project ID, used below as
GIGS_PROJECT - An API bearer token scoped to that project, used below as
GIGS_TOKEN - A plan: an eSIM plan on the
p15provider, with zero allowance and zero price (pln_prefix) - At least one add-on (
add_prefix) the subscription can be topped up with. In this guide, add-ons are referred to as data packages, the name users see in Connect - Optional: a deep-link scheme registered in your app, for example
your-app://connect-return
Gigs configures the plan and the add-ons during project setup. Both IDs are in the Gigs Dashboard.
Add-on IDs are static, but changing an add-on's price creates a new add-on ID. Read the catalogue at runtime instead of hard-coding add-on IDs.
Authenticate your requests
Send the API bearer token in the Authorization header on every request. The token grants full access to the project, so hold it as a secret on your backend and never embed it in a mobile app.
Authorization header
Authorization: Bearer ${GIGS_TOKEN}
A missing or invalid token returns 401 Unauthorized. Authentication covers creating and rotating keys.
Every endpoint in this guide is relative to the project base URL:
Base URL
https://api.gigs.com/projects/${GIGS_PROJECT}
Overview
Provisioning runs server to server and happens once per user: a user, a permanent subscription on the zero-allowance plan, and a free data add-on. Everything the user does after that runs through Gigs Connect, using Connect Sessions. Creating Connect Sessions covers the request fields and best practices, and Link back to your application covers the callback. Webhooks are optional and tell you about changes the user didn't trigger, such as a yearly renewal or a usage threshold.
What you build and what Gigs provides
| Area | You build | Gigs provides |
|---|---|---|
Create a user (POST /users) | Required. Server-to-server call | API endpoint |
Create the subscription (POST /subscriptions) | Required. Server-to-server call | API endpoint and automatic eSIM provisioning |
Grant a free data add-on (POST /subscriptionAddons) | Required. Server-to-server call | API endpoint and a pre-configured free add-on |
| eSIM install screen | Optional. A native screen showing the QR code and platform deep links | Connect Session with viewEsimInstallation, plus raw credentials, a hosted QR image, and deep-link URLs if you build your own |
| Re-install after a device change | Nothing | Connect Session with viewEsimInstallation intent |
| Data usage, package store, checkout, and support | Nothing | Connect Session with evergreenRoaming intent |
| Payment method and card updates | Nothing | Gigs Connect, backed by Gigs Billing |
| Webhook handler | Optional | Events delivered by Svix |
Provision the user and the eSIM
1. Create a user
Create a Gigs user the first time a customer opens the eSIM section in your app. email is the only required field.
You may use a placeholder address from your internal customer ID. The customer isn't asked for a real email at this point, and you can enrich the record later.
Creating a user
curl --request "POST" \
--url "https://api.gigs.com/projects/${GIGS_PROJECT}/users" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${GIGS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"email": "user-4821@esim.example.com"
}'
| Field | Type | Required | Notes |
|---|---|---|---|
email | string | Required | A syntactically valid address, unique within the project |
fullName | string | Optional | Can be added later through the updateUserFullName intent |
preferredLocale | string | Optional | For example fr-FR. Sets the default language of Connect screens |
A successful response after creating a user
{
"object": "user",
"id": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d",
"email": "user-4821@esim.example.com",
"emailVerified": false,
"fullName": null,
"preferredLocale": "en-US",
"status": "active",
"createdAt": "2026-05-05T09:31:39Z"
}
Store id, the Gigs user ID, against your internal user record. Every later call needs it.
A successful create always returns a status of active.
2. Add the user's country of residence
Gigs is the Merchant of Record for data-package purchases and applies the correct VAT rate per jurisdiction. Send the user's country so checkout doesn't have to ask for it.
Skip this call if you don't hold the user's country of residence. Connect then asks the user to confirm it during checkout, which adds a step to the funnel.
Adding the user's country of residence
curl --request "POST" \
--url "https://api.gigs.com/projects/${GIGS_PROJECT}/users/${USER_ID}/addresses" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${GIGS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"country": "ES",
"line1": "",
"city": ""
}'
Note that only country is needed, so line1 and city can be empty strings.
A successful response after adding an address
{
"object": "userAddress",
"id": "adr_0SNlurA049MEWV5ELDmnaqVXgTFT",
"country": "ES",
"user": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d",
"createdAt": "2026-05-05T09:31:52Z"
}
An invalid or missing country returns 422 Unprocessable Entity with a details field describing the error. Use an ISO 3166-1 alpha-2 code, for example ES, MX, US, or BR.
3. Create the evergreen subscription
The subscription is the permanent container for the user's eSIM, data packages, and usage records. Create one per user. It's never re-created, which is what makes the flow evergreen: buying a data package adds an add-on on top of the same subscription.
For evergreen roaming, create the subscription on the zero-allowance plan, not on a plan that carries data. The free allowance arrives separately as an add-on in step 4.
Replace the plan value with the zero-allowance plan ID from your project setup.
Creating the evergreen subscription
curl --request "POST" \
--url "https://api.gigs.com/projects/${GIGS_PROJECT}/subscriptions" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${GIGS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"plan": "pln_0SNlurA049MEWV6QrTbnKyC2Lp8x",
"sim": "auto",
"user": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d"
}'
| Field | Type | Required | Notes |
|---|---|---|---|
plan | string | Required | The static plan ID from project setup |
user | string | Required | The usr_ ID from step 1 |
sim | string | Optional | Set to "auto" so Gigs provisions a fresh eSIM |
A successful response after creating a subscription (abbreviated)
{
"object": "subscription",
"id": "sub_0U1WP6391FmqGW0gb3T3TGbsb8rW",
"plan": { "id": "pln_0SNlurA049MEWV6QrTbnKyC2Lp8x", "...": "..." },
"user": { "id": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d", "...": "..." },
"sim": null,
"status": "pending",
"currentPeriod": { "...": "..." },
"createdAt": "2026-05-05T09:32:13Z",
"activatedAt": null,
"firstUsageAt": null
}
Store id, the subscription ID. Every Connect Session from step 5 on needs it.
The subscription returns as pending and activates once the network provisions the eSIM, typically in 5 to 10 seconds. You don't need to wait for active before granting the add-on in step 4. You do need it before step 5, so subscribe to com.gigs.subscription.activated or check the status under GET /subscriptions/{id}.
4. Grant the free data add-on
The plan carries zero allowance, so on its own it provisions an eSIM with no usable data. If you want to provision free data to the user, it will arrive as a subscription add-on: a per-user instance (sad_) of the free add-on (add_) that Gigs configures at project setup.
Replace the addon value with the free add-on ID from your project setup.
Granting the free data add-on
curl --request "POST" \
--url "https://api.gigs.com/projects/${GIGS_PROJECT}/subscriptionAddons" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${GIGS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"addon": "add_0SNlurA049MEWV7StUcoLzD3Mq9y",
"subscription": "sub_0U1WP6391FmqGW0gb3T3TGbsb8rW",
"billing": {
"paymentBehavior": "none"
}
}'
| Field | Type | Required | Notes |
|---|---|---|---|
addon | string | Required | The static add-on product ID for the free package, from project setup |
subscription | string | Required | The sub_ ID from step 3 |
billing.paymentBehavior | enum | Optional | Gigs Billing only. Use none for the free grant so the zero-cost invoice settles automatically. |
A successful response after granting the add-on (abbreviated)
{
"object": "subscriptionAddon",
"id": "sad_0U2A1bQ7c3FmqGW0kLpZ9rstUv4m",
"addon": { "id": "add_0SNlurA049MEWV7StUcoLzD3Mq9y", "...": "..." },
"subscription": "sub_0U1WP6391FmqGW0gb3T3TGbsb8rW",
"user": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d",
"status": "pending",
"billing": { "paymentBehavior": "none", "...": "..." },
"currentPeriod": null,
"activatedAt": null,
"canceledAt": null,
"endedAt": null,
"createdAt": "2026-05-05T09:32:20Z"
}
Store id, the subscription add-on ID. Subscribe to com.gigs.subscriptionAddon.activated, or query GET /subscriptionAddons/{id}, to detect when the allowance is usable. Once the add-on is active it appears in the subscription's usageBalances.
Hand the user to Connect
5. Show the eSIM install screen
Once the subscription is active, show the install experience. Launch a Connect Session with the viewEsimInstallation intent and Connect renders the QR code, platform deep links, and step-by-step instructions. The same intent is the re-install path after a device change or a lost phone, so launch it again whenever the user needs to reinstall their eSIM. This requires a SIM exchange triggered through the API first.
Creating a Connect Session with the viewEsimInstallation intent
curl --request "POST" \
--url "https://api.gigs.com/projects/${GIGS_PROJECT}/connectSessions" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${GIGS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"callbackUrl": "your-app://esim-install-done",
"intent": {
"type": "viewEsimInstallation",
"viewEsimInstallation": {
"subscription": "sub_0U1WP6391FmqGW0gb3T3TGbsb8rW"
}
},
"user": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d"
}'
A successful response after creating a Connect Session
{
"object": "connectSession",
"id": "csn_0U2UMLEo0Z7P8Y37BOdNohpMnq0Y",
"url": "https://connect.gigs.com/checkout/entry?session=csn_0U2UMLEo0Z7P8Y37BOdNohpMnq0Y&token=...",
"callbackUrl": "your-app://esim-install-done",
"intent": { "...": "..." },
"user": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d"
}
To render the install screen natively instead, read the SIM credentials and build your own. The activated subscription's sim object carries the SIM ID.
Retrieving SIM credentials
curl --request "GET" \
--url "https://api.gigs.com/projects/${GIGS_PROJECT}/sims/${SIM_ID}/credentials" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${GIGS_TOKEN}"
A successful response with SIM credentials
{
"object": "simCredentials",
"sim": "sim_0U1WQria1FmqGW0nNQDxZqvrX7Nr",
"activationCode": "LPA:1$smdp.gigs.com$TEST-OUKOP-8JW6Y8B",
"qrCodeUrl": "https://files.gigs.com/qrcode/dev/6tu8ZcW5IBi.png",
"iosInstallUrl": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$smdp.gigs.com$TEST-OUKOP-8JW6Y8B",
"androidInstallUrl": "https://esimsetup.android.com/esim_qrcode_provisioning?carddata=LPA:1$smdp.gigs.com$TEST-OUKOP-8JW6Y8B"
}
Lead with iosInstallUrl or androidInstallUrl for a one-tap install, and fall back to qrCodeUrl or activationCode. Create a subscription describes each field and its device requirements.
Treat SIM credentials like a password. An eSIM activates only once, and qrCodeUrl is unauthenticated by design so that any device can render it. Never log it, email it, or store it beyond the user session.
6. Open the Evergreen Roaming session
Every top-up and account action after installation runs through a Connect Session with the evergreenRoaming intent. That one intent composes the data-usage overview, the package store, checkout, and support.
Creating a Connect Session with the evergreenRoaming intent
curl --request "POST" \
--url "https://api.gigs.com/projects/${GIGS_PROJECT}/connectSessions" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${GIGS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"callbackUrl": "your-app://topup-return",
"intent": {
"type": "evergreenRoaming",
"evergreenRoaming": {
"subscription": "sub_0U1WP6391FmqGW0gb3T3TGbsb8rW",
"entryPoint": "manageDataPackages"
}
},
"user": "usr_0SNlurA049MEWV4OpCwsNyC9Kn2d"
}'
| Field | Type | Required | Notes |
|---|---|---|---|
intent.evergreenRoaming.subscription | string | Required | The user's sub_ ID |
intent.evergreenRoaming.entryPoint | enum | Optional | manageDataPackages (default), browseDataPackages, or null |
Set entryPoint to browseDataPackages to open the store directly. The composed intent is the recommended path because it lets users pick their own data packages. You can also launch manageDataPackages, browseDataPackages, checkoutAddon, and requestSupport as standalone intents to compose the flow yourself.
When the session ends, Connect redirects to callbackUrl with session_id and status appended:
| Scenario | Your callbackUrl | Connect redirects the user to |
|---|---|---|
| Link back to your site | https://example.com/topup-return | https://example.com/topup-return?session_id=csn_123&status=success |
| Deep link into your app | your-app://topup-return | your-app://topup-return?session_id=csn_123&status=success |
Read the status parameter to decide what your app shows next:
status | What happened | What your app should do |
|---|---|---|
success | The user completed the action | Refresh local state, such as the usage bar, and show a confirmation screen |
user_aborted | The user closed or cancelled Connect | Return the user to your eSIM home screen |
auth_expired | The session URL was no longer valid when the user reached it | Offer a retry that creates a fresh Connect Session |
bad_configuration | The session attempted a flow the project doesn't allow | Show an error and contact support@gigs.com |
For native apps, use a deep link that your app is configured to handle. Creating Connect Sessions covers using Connect Sessions in native apps.
Subscribe to webhooks
Webhooks are optional. You can launch without them by, for example, checking GET /subscriptions/{id} and trusting Connect's callbackUrl. They earn their place when you want to react to events the user didn't trigger, such as a yearly renewal or a usage threshold.
Configure one HTTPS endpoint in the Dashboard. Events and webhooks covers endpoint setup, signature verification, retries, and event ordering.
Where to go from here
- Gigs API reference for every endpoint used above
- Creating Connect Sessions for session fields, appearance, and native-app rendering
- About Connect Sessions for every available intent
- Add-ons for the add-on catalogue, coverage profiles, and the full subscription add-on lifecycle
- Svix manual verification guide for webhook signatures
- Reach out to support@gigs.com for assistance