> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.simjuno.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.simjuno.com/_mcp/server.

# PHP SDK

> Install and use the SimJuno PHP SDK.

The SimJuno PHP SDK provides typed access to the reseller and eSIM APIs. It includes authentication, retries, timeouts, API exceptions, and request and response classes.

The Composer package name is `simjuno/sdk`, and its source is available on [GitHub](https://github.com/SimJuno-com/php-sdk).

## Install

```bash
composer require simjuno/sdk guzzlehttp/guzzle
```

The SDK requires PHP 8.1 or later and a PSR-18 HTTP client. The example installs Guzzle; you can use another PSR-18 implementation instead.

## Create a client

Create an [API key](/authentication), store it in `SIMJUNO_API_KEY`, and initialize the client. Keep the key in server-side code.

```php
<?php

use Simjuno\SimjunoClient;

$apiKey = getenv('SIMJUNO_API_KEY');
if ($apiKey === false || $apiKey === '') {
    throw new RuntimeException('SIMJUNO_API_KEY is required');
}

$client = new SimjunoClient(apiKey: $apiKey);

$balance = $client->reseller->balance();
if ($balance === null) {
    throw new RuntimeException('The balance request returned no response');
}
printf("Balance: $%.2f\n", $balance->balance / 10_000);
```

The SDK uses `https://api.simjuno.com/v1` by default and sends the API key in the `x-api-key` header.

## Work with eSIMs

The SDK follows the standard [SimJuno integration flow](/#simjuno-integration-flow). This example finds a plan, confirms its current details, and places an order:

```php
use Simjuno\Esim\Requests\OrderEsimRequest;
use Simjuno\Esim\Types\OrderEsimRequestOrderListItem;

$packageList = $client->esim->listPackages('spain');
if ($packageList === null || $packageList->packages === []) {
    throw new RuntimeException('No packages found');
}
$selectedPackage = $packageList->packages[0];
$currentPackage = $client->esim->getPackage($selectedPackage->slug);
if ($currentPackage === null) {
    throw new RuntimeException('Package not found');
}

$orderRequest = new OrderEsimRequest([
    'transactionId' => bin2hex(random_bytes(16)),
    'orderList' => [
        new OrderEsimRequestOrderListItem([
            'slug' => $currentPackage->slug,
            'count' => 1,
        ]),
    ],
]);
$order = $client->esim->orderEsim($orderRequest);
if ($order === null) {
    throw new RuntimeException('The order returned no response');
}
```

Ordering eSIMs debits your reseller balance. If you retry the same order, reuse the same `OrderEsimRequest` so its transaction ID and order list do not change.

After receiving the [`ORDER_STATUS` webhook](/webhook#order-status), retrieve the provisioning details for each returned eSIM ID:

```php
if ($order->esimIds === []) {
    throw new RuntimeException('The order returned no eSIM IDs');
}
$esim = $client->esim->getEsim($order->esimIds[0]);
if ($esim === null) {
    throw new RuntimeException('eSIM not found');
}

var_dump($esim->qrCodeUrl, $esim->shortUrl, $esim->ac);
```

### Available methods

| Task                         | Method                                    |
| ---------------------------- | ----------------------------------------- |
| Get reseller balance         | `$client->reseller->balance()`            |
| List destinations            | `$client->esim->listDestinations()`       |
| List plans for a destination | `$client->esim->listPackages($slug)`      |
| Get current plan details     | `$client->esim->getPackage($slug)`        |
| Order eSIMs                  | `$client->esim->orderEsim($request)`      |
| List ordered eSIMs           | `$client->esim->listEsims()`              |
| Get an eSIM                  | `$client->esim->getEsim($id)`             |
| Get current usage            | `$client->esim->checkUsage($id)`          |
| List top-up plans            | `$client->esim->listTopupPackages($slug)` |
| Cancel an eligible eSIM      | `$client->esim->cancelEsim($id)`          |

## Handle errors

Non-success responses throw `SimjunoApiException`. Transport and serialization failures throw `SimjunoException`:

```php
use Simjuno\Exceptions\SimjunoApiException;
use Simjuno\Exceptions\SimjunoException;

try {
    $client->reseller->balance();
} catch (SimjunoApiException $error) {
    error_log($error->getCode() . ': ' . $error->getBody());
    throw $error;
} catch (SimjunoException $error) {
    error_log($error->getMessage());
    throw $error;
}
```

## Configure retries and timeouts

Requests retry up to two times by default for transport failures and `408`, `429`, and `5xx` responses. Retries count toward your [rate limit](/rate-limits).

Set defaults when creating the client:

```php
$client = new SimjunoClient(
    apiKey: $apiKey,
    options: [
        'maxRetries' => 3,
        'timeout' => 30.0,
    ],
);
```

Override them for one request:

```php
$destinations = $client->esim->listDestinations(
    options: [
        'maxRetries' => 0,
        'timeout' => 10.0,
    ],
);
```

Timeout options are supported with Guzzle and Symfony HttpClient. Other PSR-18 clients use their own timeout configuration.