# Citrus Mobile Reseller API

_Version 2.0_

Build your own eSIM platform with pay-as-you-go connectivity in 200+ countries.

The Citrus Mobile Reseller API lets you provision, manage, and bill for eSIMs
programmatically. Build a travel eSIM storefront, IoT connectivity platform,
corporate travel solution, or any product that needs cellular data in 200+
countries.

## Quick Start

1. Create a developer account at [citrusmobile.com/developer](https://citrusmobile.com/developer).
2. Top up your reseller balance via the developer dashboard. Minimum **$10**.
3. Check available rates with `GET /rates`.
4. Provision your first eSIM with `POST /esim/provision`.
5. Fund the eSIM with `POST /esim/{iccid}/fund` — the SIM starts with a $0 wallet.

## Pricing

- **No monthly fee** — API access is free for all registered developers
- **$1.75 per eSIM** provisioning fee
- Data rates are **10% below** the retail rates shown on citrusmobile.com
- Volume-commitment discounts start at $1,000/month — contact sales

## Authentication

All requests require an API key in the `Authorization` header. API keys use
the prefix `rsk_` and are 68 characters long.

```
Authorization: Bearer rsk_a1b2c3d4e5f6...
```

### Access levels

| Operation | API Key | Balance |
|-----------|---------|---------|
| Read endpoints (list, status, rates, balance) | Required | — |
| Provision eSIM | Required | ≥ $1.75 |
| Enable data | Required | > $0 |
| Throttle speed | Required | — |
| Fund eSIM | Required | ≥ funded amount |
| Disable / Terminate | Required | — |
| Webhook management | Required | — |

## Rate Limits

API requests are limited to **100 requests per minute** per API key.
Exceeding this returns `429 Too Many Requests` with a `Retry-After` header.
Need higher limits? Email [support@citrusmobile.com](mailto:support@citrusmobile.com).

## Display Precision

`wallet_balance_usd` is always rounded **down** to the nearest cent and may
be up to ~5¢ below the exact remaining credit — never above it. Funding
`$20` will show `$19.99` right after funding; funding `$9`, `$18`, or `$27`
round-trips exactly. Your customer is never shortchanged — the SIM always
has at least the displayed amount of credit.

## Propagation Delay

Our upstream network takes ~2–3 minutes to reflect credit changes after a
fund call. `POST /fund` returns immediately and your customer can start
using data right away, but the reporting pipeline (`total_data_charged_usd`
updates, `esim.balance_low` / `esim.balance_depleted` webhooks) pauses for
**5 minutes after every fund** so the upstream propagation can settle.
Usage during the cooldown is still charged — it's reflected on the next
sync cycle after the window ends.

## Servers

- `https://citrusmobile.com/api/v2/reseller` — Production

## Authentication

**ApiKeyAuth** (http / bearer)

API key passed as a bearer token. Keys use the `rsk_` prefix and are 68 characters long. Manage keys at [citrusmobile.com/developer](https://citrusmobile.com/developer).

## Endpoints

- `POST /esim/provision` — Provision a new eSIM
- `GET /esim/list` — List your eSIMs
- `GET /esim/{iccid}` — Get eSIM details
- `POST /esim/{iccid}/enable` — Enable data on an eSIM
- `POST /esim/{iccid}/disable` — Disable data on an eSIM
- `POST /esim/{iccid}/terminate` — Terminate an eSIM
- `POST /esim/{iccid}/throttle` — Throttle SIM speed
- `POST /esim/{iccid}/fund` — Fund an eSIM wallet
- `GET /wallet/balance` — Get reseller account balance
- `GET /rates` — Get per-country data rates
- `GET /account` — Get account details
- `GET /webhooks` — List webhook endpoints
- `POST /webhooks` — Register a new webhook endpoint
- `GET /webhooks/{id}` — Get webhook detail
- `PUT /webhooks/{id}` — Update a webhook
- `DELETE /webhooks/{id}` — Delete a webhook
- `POST /webhooks/{id}/test` — Send a test event

## Endpoint Reference


### `POST /esim/provision`

**Provision a new eSIM**

Tags: eSIMs

Reserves an eSIM from inventory, assigns it to your billing group, and returns installation details including a QR code (base64 PNG data URL) and an iOS direct install URL. The SIM is created with a **$0 wallet** — call `POST /esim/{iccid}/fund` before your customer can use data. Delivery of the QR code or install URL is your responsibility (e.g. via email).

**Request body** `application/json`

- `end_user_reference` `string` — Your own customer/order ID for tracking. Surfaced in webhook payloads. (e.g. `order_789`)
- `label` `string` — Human-friendly label shown in dashboards and webhook payloads. (e.g. `Jane Smith - Tokyo`)

**Responses**

- `201` — eSIM created and reserved.
- `401` — API key is missing, invalid, or revoked.
- `402` — Reseller balance is insufficient for the requested operation.
- `503` — eSIM inventory depleted. Try again later or contact support.

### `GET /esim/list`

**List your eSIMs**

Tags: eSIMs

List all eSIMs provisioned by your account with pagination and optional status filtering. The returned items include the per-SIM `wallet_balance_usd` so you can drive a dashboard without a follow-up lookup.

**Parameters**

- `status` (query) `enum(pending | active | suspended | terminated)` — Filter by status
- `limit` (query) `integer` — Max results per page (up to 100).
- `offset` (query) `integer` — Pagination offset.

**Responses**

- `200` — eSIM list.
- `401` — API key is missing, invalid, or revoked.

### `GET /esim/{iccid}`

**Get eSIM details**

Tags: eSIMs

Get full detail for one eSIM, including a live activation state read from the network (with graceful fallback to the cached state). **`wallet_balance_usd`** is the remaining balance on this SIM's wallet in the same units you funded it with. When it hits 0, data stops. See the display-precision note in the intro. **`total_data_charged_usd`** is the lifetime data usage charged for this SIM at the rates returned by `/rates`. Use it as a running meter for per-customer throttle or quota logic.

**Parameters**

- `iccid` (path) `string` _(required)_ — The ICCID of the eSIM.

**Responses**

- `200` — eSIM detail.
- `401` — API key is missing, invalid, or revoked.
- `404` — ICCID not found or doesn't belong to your account.

### `POST /esim/{iccid}/enable`

**Enable data on an eSIM**

Tags: eSIMs

Enable cellular data. Use this to resume connectivity after a disable or suspension.

**Parameters**

- `iccid` (path) `string` _(required)_ — The ICCID of the eSIM.

**Responses**

- `200` — Data enabled.
- `401` — API key is missing, invalid, or revoked.
- `402` — Reseller balance is insufficient for the requested operation.
- `404` — ICCID not found or doesn't belong to your account.

### `POST /esim/{iccid}/disable`

**Disable data on an eSIM**

Tags: eSIMs

Disable cellular data. The eSIM remains provisioned but cannot pass data until re-enabled.

**Parameters**

- `iccid` (path) `string` _(required)_ — The ICCID of the eSIM.

**Responses**

- `200` — Data disabled.
- `401` — API key is missing, invalid, or revoked.
- `404` — ICCID not found or doesn't belong to your account.

### `POST /esim/{iccid}/terminate`

**Terminate an eSIM**

Tags: eSIMs

Permanently terminate an eSIM. Disables data and marks the profile as terminated. **This action cannot be undone.** Any remaining wallet balance is forfeited.

**Parameters**

- `iccid` (path) `string` _(required)_ — The ICCID of the eSIM.

**Responses**

- `200` — eSIM terminated.
- `400` — eSIM is already terminated. Cannot un-terminate; provision a new one.
- `401` — API key is missing, invalid, or revoked.
- `404` — ICCID not found or doesn't belong to your account.

### `POST /esim/{iccid}/throttle`

**Throttle SIM speed**

Tags: eSIMs

Set the data speed on an eSIM. Use this to implement fair-use policies (e.g., full speed for the first 2 GB/day, then throttle to 512 Kbps). Set to `NO_LIMIT` to restore full speed. Valid speeds: `NO_LIMIT`, `SPEED_100_KBPS` through `SPEED_3000_KBPS` in 100 Kbps increments, plus `SPEED_3500_KBPS`, `SPEED_4000_KBPS`, `SPEED_4500_KBPS`, and `SPEED_5000_KBPS`. Common choices: - `SPEED_500_KBPS` — basic browsing and messaging - `SPEED_1000_KBPS` — standard definition video - `SPEED_2000_KBPS` — light HD streaming

**Parameters**

- `iccid` (path) `string` _(required)_ — The ICCID of the eSIM.

**Request body** `application/json` (required)

- `speed` `enum(NO_LIMIT | SPEED_100_KBPS | SPEED_500_KBPS | SPEED_1000_KBPS | SPEED_2000_KBPS | SPEED_5000_KBPS)` _(required)_ — Throttle speed. See the full list in the description.

**Responses**

- `200` — Throttle applied.
- `400` — Invalid request body or parameters.
- `401` — API key is missing, invalid, or revoked.
- `404` — ICCID not found or doesn't belong to your account.

### `POST /esim/{iccid}/fund`

**Fund an eSIM wallet**

Tags: eSIMs

Fund an eSIM's data wallet. Deducts from your reseller account balance and credits the SIM's individual wallet. The eSIM can use data until this funded amount is consumed at the rates shown in `/rates`. Your reseller account is **not** debited again during usage — the fund call is the only charge. To top a customer up mid-trip, just call this endpoint again with the additional amount. See the **Display Precision** and **Propagation Delay** notes in the intro for details on how the balance number you get back relates to the amount you funded.

**Parameters**

- `iccid` (path) `string` _(required)_ — The ICCID of the eSIM.

**Request body** `application/json` (required)

- `amount` `number` _(required)_ — Amount in USD to fund this eSIM (deducted from your reseller account balance).

**Responses**

- `200` — eSIM funded.
- `400` — Invalid request body or parameters.
- `401` — API key is missing, invalid, or revoked.
- `402` — Reseller balance is insufficient for the requested operation.
- `404` — ICCID not found or doesn't belong to your account.

### `GET /wallet/balance`

**Get reseller account balance**

Tags: Wallet

Check your current reseller account balance and lifetime spending statistics. `data_suspended` is `true` **only** if Citrus support has manually frozen your account. A zero main-account balance does **not** disable existing eSIMs — they keep running as long as their per-SIM wallets have credit. Use the per-SIM `esim.balance_depleted` webhook to detect individual SIM cutoffs. `lifetime_usage_usd` is the total you've spent out of your reseller account (provisioning fees + every `/fund` call). It doesn't include pending top-ups.

**Responses**

- `200` — Wallet balance.
- `401` — API key is missing, invalid, or revoked.

### `GET /rates`

**Get per-country data rates**

Tags: Rates

Get per-country data rates — 10% below the retail rates on citrusmobile.com. These are the rates charged to your account as your customers consume data. Filter by country or continent.

**Parameters**

- `country` (query) `string` — Filter by ISO2 code, slug, or name (e.g. `US`, `japan`).
- `continent` (query) `string` — Filter by continent (e.g. `Europe`, `Asia`).

**Responses**

- `200` — Rate card.
- `401` — API key is missing, invalid, or revoked.

### `GET /account`

**Get account details**

Tags: Account

Returns your account details and eSIM counts.

**Responses**

- `200` — Account detail.
- `401` — API key is missing, invalid, or revoked.

### `GET /webhooks`

**List webhook endpoints**

Tags: Webhooks

List all registered webhook endpoints for your account. Also returns the full set of subscribable event names.

**Responses**

- `200` — Webhook list.
- `401` — API key is missing, invalid, or revoked.

### `POST /webhooks`

**Register a new webhook endpoint**

Tags: Webhooks

Register a new webhook endpoint. You'll receive a `signing_secret` in the response — **save it immediately as it won't be shown again**. Maximum 5 webhooks per account. Subscribe to specific events or pass `["*"]` to receive all events.

**Request body** `application/json` (required)

- `url` `string <uri>` _(required)_ — HTTPS URL that receives webhook POSTs.
- `events` `string[]` _(required)_ — Events to subscribe to, or `["*"]` for all.

**Responses**

- `201` — Webhook registered.
- `400` — Invalid request body or parameters.
- `401` — API key is missing, invalid, or revoked.

### `GET /webhooks/{id}`

**Get webhook detail**

Tags: Webhooks

**Parameters**

- `id` (path) `string` _(required)_

**Responses**

- `200` — Webhook detail.
- `401` — API key is missing, invalid, or revoked.
- `404` — Resource not found.

### `PUT /webhooks/{id}`

**Update a webhook**

Tags: Webhooks

Update URL, events, or active state. Setting `active` to `true` resets `failure_count` to 0.

**Parameters**

- `id` (path) `string` _(required)_

**Request body** `application/json` (required)

- `url` `string <uri>`
- `events` `string[]`
- `active` `boolean`

**Responses**

- `200` — Webhook updated.
- `400` — Invalid request body or parameters.
- `401` — API key is missing, invalid, or revoked.
- `404` — Resource not found.

### `DELETE /webhooks/{id}`

**Delete a webhook**

Tags: Webhooks

**Parameters**

- `id` (path) `string` _(required)_

**Responses**

- `200` — Deleted.
- `401` — API key is missing, invalid, or revoked.
- `404` — Resource not found.

### `POST /webhooks/{id}/test`

**Send a test event**

Tags: Webhooks

Send a test event to a webhook endpoint to verify it's receiving and processing correctly. The test event uses the special event type `"test"` and does not require a subscription to any specific event.

**Parameters**

- `id` (path) `string` _(required)_

**Responses**

- `200` — Test delivery result.
- `401` — API key is missing, invalid, or revoked.
- `404` — Resource not found.

## Contact

- Email: support@citrusmobile.com
- https://citrusmobile.com/developer

---

This Markdown is a server-rendered mirror of the interactive API reference at https://citrusmobile.com/developer/docs, generated from the OpenAPI spec at https://citrusmobile.com/openapi-reseller.yaml.
