MeiSIM Dealer API

REST API for authorized dealers to programmatically order eSIMs, top up SIMs, and manage their wallet.

Base URLhttps://api.meisimusa.com
Content typeapplication/json
API versionv1

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.

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_1Must start with a street number (123 Main St). A PO Box or PMB/mailbox is never a valid E911 address.
city2+ characters
state2-letter code (AZ), not the full state name
zip_code5 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:

  1. render activation_code as a QR yourself;
  2. show qr_png_url — a PNG we render, no auth needed, safe to drop straight into an <img>;
  3. or call GET /dealer/order/:orderId/qr for 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:

deliveredwe hold the line. Check qr_available before showing a QR
provisioningthe carrier hasn't issued it yet — poll, don't treat as an error
unavailableorder 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_codewe hold the LPA string. qr_png_url always renders, and the one-tap install links work.
carrierthe 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" }
StatusMeaning
400Bad request — missing or malformed fields
401Unauthorized — missing or invalid x-dealer-key / JWT
402Payment required — insufficient wallet balance
403Forbidden — account not approved or suspended, or the carrier is not available on your account
404Not found — order, product, or dealer doesn't exist
429Rate limit exceeded
500Server error — try again, contact support if persistent

Rate limits (per dealer unless noted)

Endpoint groupLimit
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 send5 / 15 min per IP
OTP verify10 / 15 min per IP
Password login8 / 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:

HTTPCodeMeaning
400No LPAs providedEmpty lpas array
402INSUFFICIENT_FUNDSWallet balance below required total — response.balance and response.required show details
403Dealer 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