MeiSIM Dealer API
REST API for authorized dealers to programmatically order eSIMs, top up SIMs, and manage their wallet.
| Base URL | https://api.meisimusa.com |
|---|---|
| Content type | application/json |
| API version | v1 |
Quick start
# 1. Get your API key (one-time)
curl -X POST https://api.meisimusa.com/dealer/send-otp \
-H 'content-type: application/json' \
-d '{"email":"you@yourcompany.com"}'
# Check email for the 6-digit code, then:
curl -X POST https://api.meisimusa.com/dealer/verify-otp \
-H 'content-type: application/json' \
-d '{"email":"you@yourcompany.com","code":"123456"}'
# → returns { "verified": true, "apiKey": "..." }
# 2. Use the API key on every request
curl https://api.meisimusa.com/api/v1/me \
-H 'x-dealer-key: YOUR_API_KEY'
Public catalogue — no key required
Reading the plan catalogue needs no dealer key and no signup:
GET https://api.meisimusa.com/mm/products.
It is documented in full on the public page — Catalogue API →
(endpoint, filters, response shape and fair-use limits).
Authentication
Everything below this point is the dealer API and does need a key. Two equivalent auth methods. Use either on any endpoint.
Option A — API Key (recommended for backend integration)
Issued via the OTP flow below. Passed in the request header:
x-dealer-key: YOUR_API_KEY
The key does not expire. Treat it like a password.
Option B — JWT (for portal/UI integrations)
Issued via the password-login flow. Passed in the standard Bearer header:
Authorization: Bearer YOUR_JWT
JWT expires after 12 hours. Re-login to refresh.
Auth flow — OTP login
POST/dealer/send-otp
Send a 6-digit code to your email.
{ "email": "you@yourcompany.com" }
Response: { "sent": true }. Rate limit: 5 sends per 15 min per IP. Code expires in 5 minutes; max 5 verification attempts.
POST/dealer/verify-otp
Verify the code and receive your API key.
{ "email": "you@yourcompany.com", "code": "123456" }
Response:
{ "verified": true, "apiKey": "dk_..." }
Rate limit: 10 verifications per 15 min per IP.
Auth flow — password login (returns JWT)
POST/dealer/auth/set-password
One-time password setup. Requires x-dealer-key from the OTP flow.
{
"email": "you@yourcompany.com",
"password": "min-8-chars"
}
Response: { "ok": true }.
POST/dealer/auth/login
Email + password → JWT.
{ "email": "you@yourcompany.com", "password": "..." }
Response:
{
"ok": true,
"token": "eyJhbGc...",
"expiresAt": "2026-05-06T12:00:00.000Z",
"dealer": { "id": "...", "email": "...", "company": "..." }
}
Rate limit: 8 attempts per 15 min per IP.
eSIM endpoints
GET/api/v1/esim/countries
List supported countries. Returns the upstream country catalog.
POST/api/v1/esim/bundles
List bundles for a country/region.
{
"countryISO": "JP", // optional, ISO 3166-1 alpha-2
"countryName": "Japan", // optional, alternative to ISO
"region": "Asia", // optional
"pageNumber": 1, // optional, default 1
"pageSize": 20 // optional, default 20, max 100
}
Response: paginated bundle list with each bundle's name, data allowance, validity, and price.
POST/api/v1/esim/order
Order an eSIM for an end customer.
{
"bundleName": "esim_5gb_30days_jp", // required, from /bundles
"customerReference": "ORDER-12345", // optional, your tracking id
"emailAddress": "end-customer@x.com", // optional, where the QR is sent
"qrToCustomer": true // optional, default false
}
Response: { "Status": "Success", "Iccid": "...", "QrCode": "...", ... } (upstream-format). The order is logged in your dealer history.
POST/api/v1/esim/details
Get activation status and data usage.
{ "iccid": "894900..." }
Returns activation state, data used vs allowance, expiry.
POST/api/v1/esim/revoke
Deactivate an eSIM upstream and update your dealer order history.
{ "iccid": "894900..." }
Rate limit: 10 per minute per dealer.
Top-up endpoints
For recharging existing SIMs (not new eSIMs).
GET/api/v1/topup/networks
List networks eligible for top-up.
POST/api/v1/topup/confirm
Validate a target SIM exists on a network. Use this before recharge to avoid charging a non-existent SIM.
{
"networkName": "Vodafone-UK",
"contactNumber": "+447700900000", // either contactNumber
"simSerialNumber": "894400..." // OR simSerialNumber
}
POST/api/v1/topup/recharge
Execute a top-up recharge.
{
"networkName": "Vodafone-UK", // required
"contactNumber": "+447700900000", // OR simSerialNumber
"simSerialNumber": "894400...",
"topUpValue": 20, // required, currency depends on network
"bundleValue": null, // optional bundle add-on
"bundleProductCode": null
}
Account & history
GET/api/v1/me
Whoami.
{ "id": "...", "company_name": "...", "email": "...", "status": "approved" }
GET/api/v1/orders
Recent orders. Returns up to the last 100, newest first.
[
{
"id": "...",
"order_type": "esim" | "topup",
"bundle_name": "esim_5gb_30days_jp",
"customer_reference": "ORDER-12345",
"email_address": "end-customer@x.com",
"iccid": "894900...",
"amount": 6.50,
"status": "completed",
"created_at": "2026-05-05T12:34:56Z"
}
]
Wallet
GET/dealer/wallet
Wallet balance and recent transactions.
{
"ok": true,
"balance": 245.30,
"markupPct": 15,
"resendFee": 1.00,
"company": "...",
"email": "...",
"transactions": [
{
"id": "...",
"created_at": "2026-05-05T12:34:56Z",
"type": "debit" | "topup",
"amount_usd": -6.50,
"balance_after_usd": 245.30,
"description": "...",
"stripe_session_id": null,
"related_order_id": "..."
}
]
}
GET/dealer/statement
CSV export. Default range: last 90 days.
GET /dealer/statement?from=2026-04-01&to=2026-05-01
Returns CSV with columns: Date, Type, Amount USD, Balance after USD, Description, Order ID, Stripe session.
POST/dealer/topup
Create Stripe checkout session for wallet top-up.
{ "amountUsd": 100 }
amountUsd must be between $50 and $10,000. Response: { ok, checkoutUrl, netAmt, grossAmt, fee }. Redirect user to checkoutUrl. Rate limit: 5/min.
POST/dealer/topup-intent
Create Stripe PaymentIntent for embedded checkout.
{ "amountUsd": 100 }
Response: { ok, clientSecret, publishableKey, netAmt, grossAmt, fee }. Use Stripe.js with clientSecret. Rate limit: 10/min.
GET/dealer/topup/preview?net=100
Preview Stripe fee. Response: { ok, net, gross, fee }.
Order from your wallet
POST/dealer/order
Alternative to /api/v1/esim/order — debits your wallet at your dealer price.
Moxee plans need imei but no eid. PrepaidIQ issues the Moxee eSIM profile from their own pool, so there is no customer EID to bind — send one and it is ignored. The customer's 15-digit device IMEI is required. Every other US prepaid (p3:) carrier still requires both.
Changed 2026-08-10 — breaking. Moxee previously needed neither field, because we activated it onto a SIM from our own stock. Moxee orders sent without an imei now fail with a 400.
{
"productId": "p3:att-prepaid-30", // required
"customerEmail": "end@x.com", // required
"customerName": "Jane Doe", // required
"quantity": 1, // optional, 1–20
"language": "en", // optional
"imei": "359123456789012", // required for ALL US prepaid, Moxee included
"eid": "89001012...", // required for US prepaid — NOT for Moxee
"whatsappNumber":"+13055550123", // optional — see below
"address": { // optional — see below
"first_name": "Jane",
"last_name": "Doe",
"address_line_1": "123 Main St",
"city": "Phoenix",
"state": "AZ",
"zip_code": "85001",
"phone": "+15555550100"
}
}
Carriers your account cannot activate
Some US carriers can be switched off for dealer accounts — across the board or for one account — usually because the carrier's own fraud team has raised something about traffic on those lines. When that applies to you, the carrier's plans do not appear in the catalogue you are served, and /dealer/order (and /dealer/replace/:orderId) answers 403 with "<Carrier> activations are not available on your account." Nothing is charged and no line is created. Refills of lines you already sold on that carrier keep working — only new activations are refused.
2026-08-12 — LinkUp Mobile is blocked for all dealer accounts. AT&T's fraud department flagged LinkUp lines activated through us as SMS spamming. Existing LinkUp lines are unaffected and can still be recharged.
WhatsApp delivery (whatsappNumber)
Optional. When set, your customer also gets the assigned number, the QR and the one-tap install links on WhatsApp, the moment the line goes live. Email is sent either way — this adds a channel, it never replaces one.
- Include the country code —
+1 305 555 0123,447700900123. Spaces and punctuation are ignored. A bare 10-digit number is assumed to be US; anything else without a country code is rejected, because guessing one would deliver a stranger your customer's eSIM. - Must be 8–15 digits including the country code. An unusable number is ignored, not an error — the order still completes and the eSIM still goes out by email.
- This is your customer's number. Your own alerts are separate and per-event — see
/dealer/notify-prefs. - Only send a number the customer asked you to use.
The activation address (US p3: plans)
address is optional, and it does two jobs at once: it is registered with the carrier as the line's E911 (emergency services) address, and the carrier picks the number's area code from the ZIP — so a Phoenix ZIP produces a Phoenix number. Omit address entirely and we assign both for you.
If you send any of address_line_1, city, state or zip_code, you must send all four, valid. A partial address can't be used for either job, so the order is rejected with 400 rather than quietly activating on our address (which would give your customer an unexpected area code).
address_line_1 | Must start with a street number (123 Main St). A PO Box or PMB/mailbox is never a valid E911 address. |
|---|---|
city | 2+ characters |
state | 2-letter code (AZ), not the full state name |
zip_code | 5 digits (ZIP+4 accepted, extra digits ignored) |
first_name / last_name / address_line_2 / phone are always optional, and an address carrying only names is fine — it just names the customer.
We do not pre-validate the address against the carrier's E911 database (no carrier exposes one to resellers) — it is checked at activation. If the carrier rejects it, we automatically retry once with our own address so the line still activates; the area code then follows ours, not the one you asked for.
Response:
{
"ok": true,
"orderId": "...",
"shortId": "MM-AB12CD",
"unitPriceUsd": 25.00,
"totalUsd": 25.00,
"newBalance": 220.30,
"activation": "delivered" // "delivered" | "pending" | "failed"
}
Returns 402 Payment Required if balance is insufficient.
Branch on activation, not on ok. ok: true means the order was accepted and priced — the carrier can still refuse the activation after that. pending means the carrier has it and hasn't confirmed yet; we deliver automatically when it does.
When activation is "failed" the response also carries a failure object — the carrier's verdict, what it means, and the one thing that changes the outcome on a retry. Show whatToDo to whoever is holding the handset:
{
"ok": true,
"shortId": "MeiDlr-5WTVYMEKJUH2",
"activation": "failed",
"failure": {
"reason": "The device is compatible for esim activation through IMEI2. Please use IMEI2 for esim activation.",
"meaning": "The phone is fine — we were given the IMEI for its physical SIM slot, not its eSIM.",
"whatToDo": "Dial *#06# on the handset. Dual-SIM phones list TWO IMEIs — use the SECOND one (IMEI2), which is the eSIM. Place the order again with that number and it should go through.",
"refunded": true
}
}
refunded: true means the charge is already back in your wallet and there is nothing to claim. The one verdict to not retry blindly is a carrier timeout — there we cannot tell whether a line was created, and whatToDo says so.
On a successful order this response carries no eSIM data — take the orderId (or shortId) and call the endpoint below to get the QR / activation code.
GET/dealer/order/:orderId
The order's eSIM: QR, activation code, ICCID and assigned US number. Accepts the UUID or the short id (MeiDlr-…), and is scoped to your own orders. One entry per line, so a quantity: 3 order returns three.
{
"ok": true,
"orderId": "…", "shortId": "MeiDlr-3NSXHFD8THR2",
"productId": "p3:318:698", "state": "fulfilled", "quantity": 1,
"customer": { "email": "end@x.com", "name": "Jane Doe" },
// on a failed order only — same shape as POST /dealer/order's `failure`,
// alongside the raw carrier string in `error`
// "failure": { "reason": "…", "meaning": "…", "whatToDo": "…" },
"lines": [{
"line": 1,
"status": "delivered",
"activation_code": "LPA:1$CUST-001-V4-PROD-ATL2.GDSB.NET$EAF6DA2AB…",
"smdp": "CUST-001-V4-PROD-ATL2.GDSB.NET",
"matching_id": "EAF6DA2AB…",
"iccid": "89049032000001000096650386218347",
"phone_number": "3415128315",
"qr_png_url": "https://api.meisimusa.com/qr?size=480&text=…",
"install_ios_url": "https://esimsetup.apple.com/…",
"install_android_url": "https://esimsetup.android.com/…"
}]
}
About the QR code
The carrier issues an activation string, not a picture. activation_code is the GSMA LPA:1$<smdp>$<matchingId> payload, and a QR code is simply that string encoded. This holds for LinkUp Mobile and every other US carrier. Three ways to deliver it:
- render
activation_codeas a QR yourself; - show
qr_png_url— a PNG we render, no auth needed, safe to drop straight into an<img>; - or call
GET /dealer/order/:orderId/qrfor the PNG bytes over your API key —?line=N(default 1) and?size=(64–1024, default 480).
install_ios_url / install_android_url are one-tap install links: on iOS 17.4+ and recent Android, tapping installs the eSIM with no scan at all.
status is one of three, and your next move differs for each:
delivered | we hold the line. Check qr_available before showing a QR |
|---|---|
provisioning | the carrier hasn't issued it yet — poll, don't treat as an error |
unavailable | order finished with nothing recorded. Won't change on its own — contact us |
Gate your QR rendering on qr_available, not on status. qr_source says where the QR comes from, and it matters:
activation_code | we hold the LPA string. qr_png_url always renders, and the one-tap install links work. |
|---|---|
carrier | the carrier holds the profile and serves its QR image (Moxee, LinkUp). qr_png_url fetches it on demand — handle a failed image load, since an older line may have no profile behind it. activation_code and the one-tap links are null; the customer scans the QR. |
The QR route answers 409 when there is no QR to give, and 404 only when the line does not exist.
Cancelled or refunded orders return no install data (lines: [], and the QR route answers 410) — their profiles are revoked at the carrier.
POST/dealer/resend-email/:orderId
Resend an eSIM activation email. Charges the resend fee (~$1).
{ "email": "alternate@x.com" }
Body is optional — omit email to resend to the original address.
POST/dealer/replace/:orderId
Issue a replacement eSIM at the original product's current price. Charged from your wallet. Returns 402 if insufficient balance.
Errors
All errors return JSON:
{ "error": "Invalid request", "message": "amountUsd must be between 50 and 10000" }
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or malformed fields |
| 401 | Unauthorized — missing or invalid x-dealer-key / JWT |
| 402 | Payment required — insufficient wallet balance |
| 403 | Forbidden — account not approved or suspended, or the carrier is not available on your account |
| 404 | Not found — order, product, or dealer doesn't exist |
| 429 | Rate limit exceeded |
| 500 | Server error — try again, contact support if persistent |
Rate limits (per dealer unless noted)
| Endpoint group | Limit |
|---|---|
Order operations (/api/v1/esim/order, /api/v1/topup/recharge, /dealer/order) | No limit — order as many as your wallet balance covers |
Read operations (/api/v1/me, /api/v1/orders, /api/v1/esim/countries, /api/v1/esim/bundles, /api/v1/esim/details, /api/v1/topup/networks, /api/v1/topup/confirm, /api/v1/esim/revoke) | 600 / min |
Wallet top-ups (/dealer/topup, /dealer/topup-intent) | 5–10 / min |
| OTP send | 5 / 15 min per IP |
| OTP verify | 10 / 15 min per IP |
| Password login | 8 / 15 min per IP |
Rate-limit window is sliding; on 429, wait the indicated period and retry.
eSIM Verification — Dealer API
Submit a batch of LPAs (eSIM activation codes) and we'll classify each as one of:
available, used, invalid, error, unknown.
The check is read-only — LPAs are never consumed by the verification.
Batches up to 50,000 rows. Results typically arrive within minutes.
Pricing: $1 per LPA, debited from your dealer wallet at submission time.
Rows that return error are refunded automatically — you only pay for successful verdicts.
Authentication uses either your bearer JWT (from /dealer/login) or the legacy x-dealer-key header.
POST /dealer/esim-verify — Submit batch
POST https://api.meisimusa.com/dealer/esim-verify
Authorization: Bearer <dealer-jwt> # OR: x-dealer-key: <your-key>
Content-Type: application/json
{
"lpas": [
"LPA:1$T-MOBILE.IDEMIA.IO$AYU36-O48VE-8PWDE-ZRXGS",
"LPA:1$T-MOBILE.IDEMIA.IO$O0VQX-7MJVW-99Y97-KLSI4",
"..."
],
"notify_email": "results@yourcompany.com" // optional
}
Response (200):
{
"ok": true,
"batch_id": "<uuid>",
"total_rows": 2,
"charged_usd": 2.00,
"wallet_balance_usd": 148.00
}
Errors:
| HTTP | Code | Meaning |
|---|---|---|
| 400 | No LPAs provided | Empty lpas array |
| 402 | INSUFFICIENT_FUNDS | Wallet balance below required total — response.balance and response.required show details |
| 403 | — | Dealer account not active |
GET /dealer/esim-verify/:batch_id — Poll batch
GET https://api.meisimusa.com/dealer/esim-verify/<batch-uuid>
Authorization: Bearer <dealer-jwt>
200 OK
{
"ok": true,
"batch": {
"id": "<uuid>",
"label": "Dealer Acme Inc · 100 LPAs",
"total_rows": 100,
"started_at": "2026-05-13T18:00:00Z",
"completed_at": null,
"paid_total_usd": 100.00
},
"progress": {
"total": 100,
"pending": 40,
"in_progress": 5,
"used": 35,
"available": 18,
"invalid": 1,
"error_count": 1,
"unknown": 0
}
}
GET /dealer/esim-verify/:batch_id/results.csv — Download CSV
Returns a CSV with columns iccid, lpa, status, reason, checked_at.
Safe to call while the batch is still running — pending rows show status=pending.
curl -H "Authorization: Bearer $DEALER_JWT" \
https://api.meisimusa.com/dealer/esim-verify/<batch-uuid>/results.csv \
-o results.csv
eSIM Supply — Portal API
Authentication is JWT bearer (not the x-dealer-key header above).
POST to /api/portal/v1/auth/login with the credentials we issued you to
receive a token. All endpoints in this section share the prefix
/api/portal/v1. Reseller portal access is by invitation only —
contact support@meisimusa.com.
eSIM Supply sells pre-validated AT&T eSIM stock. Each purchase atomically claims rows
from inventory using FOR UPDATE SKIP LOCKED, so two concurrent buyers never
collide on the same iccid. Wallet is debited per unit at your tier's price.
Before any purchase, read the eSIM Supply Terms of Service — in particular the rule that removing an eSIM as a troubleshooting step permanently destroys it and disqualifies it from replacement.
POST /services/esim_supply_att/execute — Buy eSIMs
Atomically claims quantity rows from inventory and assigns them to the caller.
POST /api/portal/v1/services/esim_supply_att/execute
Authorization: Bearer <jwt>
Content-Type: application/json
{
"sku_id": "<sku-uuid-from-/services/esim_supply_att/skus>",
"quantity": 10
}
Response (200):
{
"usage_id": 12345,
"vendor_order_ref": "INV-12345",
"cost_usd": 40.00,
"status": "succeeded",
"result": {
"carrier": "att",
"plan_type": "qr",
"quantity_ordered": 10,
"quantity_filled": 10,
"partial": false,
"esims": [
{ "iccid": "8901410...", "lpa": "LPA:1$...", "inventory_id": "...", "assigned_at": "..." },
...
]
}
}
If stock is short, quantity_filled < quantity_ordered, partial
is true, and the wallet is automatically credited for the shortfall.
If stock is zero, the call fails with VENDOR_ERROR and the full debit is refunded.
GET /esim-supply/stock — Stock count
Returns available counts so your UI can hide "Buy 1000" when only 12 are left.
GET /api/portal/v1/esim-supply/stock
Authorization: Bearer <jwt>
200 OK
{ "stats": { "att.qr.available": 12345, "att.qr.sold": 678, "att.qr.burned": 23 } }
GET /esim-supply/my — My eSIMs
Lists eSIMs assigned to the caller, newest first. Paged via limit and offset.
GET /api/portal/v1/esim-supply/my?limit=100&offset=0
Authorization: Bearer <jwt>
200 OK
{
"esims": [
{
"id": "...",
"iccid": "8901410...",
"lpa": "LPA:1$...",
"carrier": "att",
"plan_type": "qr",
"status": "sold",
"assigned_at": "2026-05-12T10:00:00Z",
"sold_via_usage_id": 12345
}
],
"total": 42,
"limit": 100,
"offset": 0
}
POST /esim-supply/replace — Request replacement
Submit an iccid you believe is broken. We enqueue a validator check on the matching LPA,
respond synchronously with a check_id, and you poll /esim-supply/replace/status/:check_id
until decision is not pending.
POST /api/portal/v1/esim-supply/replace
Authorization: Bearer <jwt>
{ "iccid": "8901410..." }
200 OK
{ "ok": true, "decision": "submitted", "check_id": "<uuid>", "message": "..." }
GET /api/portal/v1/esim-supply/replace/status/:check_id
Authorization: Bearer <jwt>
200 OK (one of)
{ "decision": "pending", "validator_status": "in_progress" }
{ "decision": "install_again", "validator_status": "available", "message": "..." }
{ "decision": "replaced", "validator_status": "used", "replacement": { "iccid": "...", "lpa": "...", "inventory_id": "..." } }
{ "decision": "awaiting_restock", "validator_status": "used", "message": "..." }
{ "decision": "admin_review", "validator_status": "error", "message": "..." }
Replacement policy: only validator-confirmed used verdicts auto-replace
from current inventory. available means the eSIM is still valid — try installing again.
Anything else routes to admin review within 24h.
eSIM Validator — Portal API
Submit a CSV of LPAs and we run each through a real eSIM-capable device on our worker
pool. Each row returns one of: available, used, invalid,
error, unknown. Median throughput is ~5s per LPA.
Pricing is per-row at the price shown in /services/esim_validate/skus.
The wallet is debited up-front for cost_per_row × rows; rows that error
out are refunded automatically — you only pay for successful verdicts.
POST /services/esim_validate/execute — Submit batch
POST /api/portal/v1/services/esim_validate/execute
Authorization: Bearer <jwt>
Content-Type: application/json
{
"sku_id": "<sku-uuid>",
"label": "att-batch-may12",
"csv": "iccid,lpa\n8901410...,LPA:1$...\n..."
}
Response (200) — batch is queued; status is executing until workers finish.
{
"usage_id": 54321,
"vendor_order_ref": "<batch-uuid>",
"cost_usd": 150.00,
"status": "executing",
"result": {
"batch_id": "<batch-uuid>",
"total_rows": 1000,
"cost_usd": 150.00,
"per_row_usd":0.15,
"message": "Batch queued. Poll /esim-check/customer/batches/:id for progress."
}
}
GET /esim-check/customer/batches/:id — Poll batch
GET /api/portal/v1/esim-check/customer/batches/<batch-uuid>
Authorization: Bearer <jwt>
200 OK
{
"batch": {
"id": "<uuid>",
"label": "att-batch-may12",
"total_rows": 1000,
"started_at": "...",
"completed_at": null,
"paid_total_usd": 150.00
},
"progress": {
"total": 1000,
"pending": 400,
"in_progress": 8,
"used": 350,
"available": 230,
"invalid": 8,
"error_count": 2,
"unknown": 2
}
}
GET /esim-check/customer/batches/:id/results.csv — Download results
Returns a CSV with columns iccid, lpa, status, reason, checked_at.
Safe to call while the batch is still running — pending rows show status=pending.
curl -H "Authorization: Bearer $TOKEN" \
https://api.meisimusa.com/api/portal/v1/esim-check/customer/batches/<batch-uuid>/results.csv \
-o results.csv
Code samples
JavaScript / Node.js
const API = 'https://api.meisimusa.com';
const KEY = process.env.MEISIM_DEALER_KEY;
async function orderEsim(bundleName, customerEmail) {
const r = await fetch(`${API}/api/v1/esim/order`, {
method: 'POST',
headers: {
'x-dealer-key': KEY,
'content-type': 'application/json',
},
body: JSON.stringify({
bundleName,
emailAddress: customerEmail,
qrToCustomer: true,
}),
});
if (!r.ok) throw new Error(`HTTP ${r.status}: ${await r.text()}`);
return r.json();
}
Python
import os, requests
API = 'https://api.meisimusa.com'
KEY = os.environ['MEISIM_DEALER_KEY']
def order_esim(bundle_name, customer_email):
r = requests.post(
f'{API}/api/v1/esim/order',
headers={'x-dealer-key': KEY},
json={
'bundleName': bundle_name,
'emailAddress': customer_email,
'qrToCustomer': True,
},
timeout=30,
)
r.raise_for_status()
return r.json()
curl
curl -X POST https://api.meisimusa.com/api/v1/esim/order \
-H "x-dealer-key: $MEISIM_DEALER_KEY" \
-H 'content-type: application/json' \
-d '{"bundleName":"esim_5gb_30days_jp","emailAddress":"end@x.com","qrToCustomer":true}'
For dealer onboarding, account questions, or technical support, contact your account manager directly.
Document version: v1.0 — 2026-05-05