> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sakawave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get started with eSIM

> Browse, quote, buy, and hand over a QR code — four calls.

```bash theme={null}
export SAKAWAVE_API_KEY='sk_test_replace_with_your_key'
export SAKAWAVE_BASE_URL='https://sandbox-api.sakawave.example'
```

You need an active, debitable account that is not a customer wallet. Any currency you hold will do.

## 1. Find a plan

Two reads, top down.

```bash theme={null}
# Where can they go
curl --silent --url "$SAKAWAVE_BASE_URL/v1/esims/catalog/regions?kind=country" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" | jq '.items[] | {code, name}'

# What is on offer there
curl --silent --url "$SAKAWAVE_BASE_URL/v1/esims/catalog/regions/US/packages" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
  | jq '.items[] | {slug, name, price: .price.display, days: .validityDays}'
```

Prices are USD. What your account pays is the next step.

<Note>
  Cache the catalog — an hour is reasonable. It is synchronised from the network and changes slowly, but re-read before you quote if your copy is old.
</Note>

## 2. Ask what it costs you

Skip this only if your account is USD.

<CodeGroup>
  ```bash cURL theme={null}
  QUOTE=$(curl --silent --show-error --fail-with-body \
    --request POST \
    --url "$SAKAWAVE_BASE_URL/v1/esims/quote" \
    --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
    --header 'Content-Type: application/json' \
    --data '{
      "packageSlug": "united-states-5gb-30d-pkgus5gb",
      "quantity": 1,
      "debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR"
    }')

  echo "$QUOTE" | jq '{pay: .payable.display, currency: .payable.currency, rate, expiresAt}'
  ```

  ```ts TypeScript theme={null}
  const quote = await post('/v1/esims/quote', {
    packageSlug: 'united-states-5gb-30d-pkgus5gb',
    quantity: 1,
    debitAccountId: account.id,
  });

  // Show this, not the USD price, when the account is not USD.
  console.log(`You pay ${quote.payable.display} ${quote.payable.currency}`);
  ```
</CodeGroup>

```json theme={null}
{
  "price":   { "amount": "999",     "currency": "USD", "display": "9.99" },
  "payable": { "amount": "1568430", "currency": "NGN", "display": "15,684.30" },
  "rate": "1570.000000000000",
  "fxQuoteId": "01K2Z3AB4W5X6Y7Z8ABCDEFGHJ",
  "expiresAt": "2026-08-29T10:32:00.000Z"
}
```

Show `payable`. That is the figure that will appear on their statement.

## 3. Buy it

```bash theme={null}
ORDER=$(curl --silent --show-error --fail-with-body \
  --request POST \
  --url "$SAKAWAVE_BASE_URL/v1/esims" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "packageSlug": "united-states-5gb-30d-pkgus5gb",
    "quantity": 1,
    "debitAccountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR",
    "fxQuoteId": "01K2Z3AB4W5X6Y7Z8ABCDEFGHJ",
    "externalReference": "esim_20260829_001"
  }')

echo "$ORDER" | jq '{status, paid: .paid.display, profileIds}'
```

You will almost certainly get `provisioning` with an empty `profileIds`. That is correct — the money has moved and the network is allocating.

<Warning>
  `externalReference` is your idempotency key. Reuse it on a retry and you get the original order back; use a fresh one and you buy a second eSIM.
</Warning>

## 4. Wait for it, then hand it over

Take the webhook if you can:

```json theme={null}
{ "event": "esim.order.completed", "data": { "status": "completed", "profileIds": ["01K2Z3AB4R5S6T7V8WXYZABCDE"] } }
```

Or poll the order every few seconds until it settles:

```ts theme={null}
let order = created;
while (order.status === 'provisioning') {
  await sleep(3_000);
  order = await get(`/v1/esims/orders/${order.id}`);
}
if (order.status !== 'completed') throw new Error(order.failureReason ?? 'not issued');
```

Then fetch what the phone needs:

```bash theme={null}
curl --silent --url "$SAKAWAVE_BASE_URL/v1/esims/01K2Z3AB4R5S6T7V8WXYZABCDE/installation" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" | jq
```

```json theme={null}
{
  "universalLinkUrl": "https://esim.example.com/i/K2-1A2B3C4D5E",
  "qrCodePayload": "LPA:1$smdp.example.com$K2-1A2B3C4D5E",
  "manual": { "smdpAddress": "smdp.example.com", "activationCode": "K2-1A2B3C4D5E" }
}
```

Offer the link first, the QR second, the manual details last. The QR has to be scanned from a *different* device than the one showing it.

## What next

* [Watch data run down](/api-reference/esim/get-live-usage), or take `esim.usage.low` and `esim.expiry.near`.
* [Add data](/api-reference/esim/add-data-to-an-esim) instead of selling a second eSIM.
* [Setup and configuration](/services/esim/setup-and-configuration) for accounts, quotes, and how failures refund.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.