---
title: "Purchase an eSIM (or topup with parent_order_id)"
method: POST
path: "/esims"
tags: ["eSIM"]
---

# Purchase an eSIM (or topup with parent_order_id)

`POST /esims`

## Request

- Base URL: `https://dashboard.voidmob.com/api/v1`
- URL: `https://dashboard.voidmob.com/api/v1/esims`
- Auth: HTTP bearer

## Headers

- `Idempotency-Key` string, required

## Request body

- EsimCreateBody
  - `product_id` string, required: Public product id (prefixed `prod_`) from the eSIM catalog.
  - `max_price_cents` integer: Optional price ceiling in USD cents. If the current price exceeds this value the call returns `409 PRICE_OVER_CAP`. Omit to accept any price.
  - `parent_order_id` string: Public id of the eSIM to top up (prefixed `esim_`). Any order on that eSIM is accepted - a topup id resolves to the base order it sits on. Ignored on `POST /esims/{id}/topups` where the path parameter takes precedence.
  - `webhook_endpoint_id` string: If set, scope this order's webhook events to this endpoint only.

## Response `201`

Created. Returns the new eSIM order resource.

- EsimEnvelope
  - `success` true, required
  - `data` object, required
    - `esim` EsimResource, required
      - `id` string, required: Public eSIM order identifier (prefixed `esim_`).
      - `status` 'processing' | 'completed' | 'cancelled' | 'refunded' | 'expired', required: Current lifecycle state of the eSIM order.
      - `product_id` string, nullable, required: Public product id this order was placed for (prefixed `prod_`). Null for legacy orders.
      - `is_topup` boolean, required: Whether this order is a data topup on an existing eSIM.
      - `parent_order_id` string, nullable, required: Public id of the parent eSIM order when `is_topup` is true. Null for primary orders.
      - `iccid` string, nullable, required: ICCID (SIM card identifier). Present on primary completed orders; null for topups and processing orders.
      - `activation_code` string, nullable, required: SM-DP+ matching ID used by the device to install the profile. Combine with `smdp_address` to form the full LPA URI: `LPA:1$<smdp_address>$<activation_code>`. The PNG returned from `qr_code_url` already encodes the full URI for direct scanning. Null for topups and processing orders.
      - `qr_code_url` string, nullable, required: URL to a PNG image of the activation QR code. Null for topups and orders that have not completed. Cache aggressively - this URL is stable for the lifetime of the eSIM.
      - `smdp_address` string, nullable, required: SM-DP+ server hostname. Combine with `activation_code` to build the LPA URI for manual activation. Null for topups and processing orders.
      - `data_limit_gb` number, nullable, required: Data allowance in GB. Null when `data_unlimited` is true.
      - `data_unlimited` boolean, required: Whether this plan includes unlimited data.
      - `validity_days` integer, required: Plan validity in days from activation.
      - `countries` string[], required: ISO-3166-1 alpha-2 country codes (uppercase) where the plan is usable.
      - `routing_location` string, nullable, required: Network routing hint for this eSIM (e.g. country where its data egresses). Informational only.
      - `charged_price_cents` integer, required: Amount deducted from the wallet for this order in USD cents.
      - `currency` 'USD', required
      - `created_at` string, date-time, required: ISO 8601 timestamp when the order was placed.
      - `completed_at` string, date-time, nullable, required: ISO 8601 timestamp when the eSIM was fully provisioned. Null until status is `completed`.
      - `expires_at` string, date-time, nullable, required: ISO 8601 timestamp when the eSIM plan expires. Null until the eSIM is activated.

## Other responses

- `202`: Accepted. eSIM is still provisioning. Poll `GET /esims/{id}` or wait for `esim.created` webhook.
- `400`: 400 - Request failed validation.
- `401`: 401 - Authentication required.
- `402`: 402 - Your wallet balance is too low for this purchase.
- `404`: Not found. Body discriminates by `error.code`: - `PRODUCT_NOT_FOUND` - the `product_id` is unknown or hidden - `PARENT_ORDER_NOT_FOUND` - the `parent_order_id` does not exist or belongs to another user
- `409`: Conflicts. Body discriminates by `error.code`: - `IDEMPOTENCY_CONFLICT`, `IDEMPOTENCY_REPLAY_IN_FLIGHT` - `TOPUP_INCOMPATIBLE`, `PARENT_ORDER_NOT_ACTIVE` - `PRICE_OVER_CAP` - current price exceeds `max_price_cents`; see `PriceOverCapError`
- `429`: 429 - Rate limit exceeded. Retry after the indicated interval.
- `500`: 500 - An unexpected error occurred. Please try again.
- `502`: 502 - Service error. Please retry shortly.
- `503`: 503 - Product is temporarily unavailable.

---

[API](https://skmtc.dev/voidmob/apis/voidmob-public-api.md) · [All operations](https://skmtc.dev/voidmob/apis/voidmob-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/voidmob/apis/voidmob-public-api/revisions/066023313ebe?raw)
