MeiSIM Dealer API
REST API for authorized dealers to programmatically order eSIMs, top up SIMs, and manage their wallet.
| Base URL | https://meisimusa.com |
|---|---|
| Content type | application/json |
| API version | v1 |
Quick start
# 1. Get your API key (one-time)
curl -X POST https://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://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://meisimusa.com/api/v1/me \
-H 'x-dealer-key: YOUR_API_KEY'
Authentication
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.
{
"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 US prepaid (15 digits)
"eid": "89001012...", // required for US prepaid (hex)
"address": {
"first_name": "Jane",
"last_name": "Doe",
"address_line_1": "123 Main St",
"city": "Phoenix",
"state": "AZ",
"zip_code": "85001",
"phone": "+15555550100"
}
}
Response:
{
"ok": true,
"orderId": "...",
"shortId": "MM-AB12CD",
"unitPriceUsd": 25.00,
"totalUsd": 25.00,
"newBalance": 220.30
}
Returns 402 Payment Required if balance is insufficient.
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 |
| 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 |
|---|---|
Read operations (/api/v1/me, /api/v1/orders, /api/v1/esim/countries, /api/v1/esim/bundles, /api/v1/esim/details, /api/v1/topup/networks) | 60 / min |
Order operations (/api/v1/esim/order, /api/v1/topup/confirm, /api/v1/topup/recharge, /dealer/order) | 30 / min |
Revocations (/api/v1/esim/revoke) | 10 / 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.
Code samples
JavaScript / Node.js
const API = 'https://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://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://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