Everything your partner panel does, from your own software: eSIMs, numbers, SIP trunks, your balance, rate cards and signed webhooks — in a live and a sandbox environment.
FormatJSON in, JSON out. Amounts are integer cents.
AuthYour key in X-Api-Key
Sandboxmtp_test_… keys, play money, every endpoint
One API serves your software and your partner panel: anything a person of your team can do in the panel, your code can do with a key. Every object belongs to your account; an id of another account answers “not found”.
Quickstart
Get a sandbox keyIn your partner panel: Developers → Sandbox → New key. It is shown once — keep it in your secrets.
Then switch the key to mtp_live_… — the same code, real eSIMs, your real balance.
Authentication
Send your key in the X-Api-Key header on every request. Keys are issued in the partner panel and shown once; revoke and rotate them there. A key carries its environment and its scopes — esims:write, wallet:read… — and a request outside them answers 403 INSUFFICIENT_SCOPE naming the scope it needs.
Environments
Live · mtp_live_…Real eSIMs, numbers and calls, paid from your prepaid balance.
Sandbox · mtp_test_…The same endpoints and answers with play money and test objects. Nothing reaches an operator or your balance.
Idempotency
Every request that creates or pays needs an Idempotency-Key header — any unique string up to 120 characters. A retry with the same key and the same body gets the first answer back with Idempotent-Replayed: true: never a second order or a second charge. The same key with a different body answers 409 IDEMPOTENCY_KEY_REUSED; a key whose first request is still running answers 409 IDEMPOTENCY_IN_PROGRESS.
Errors
One shape for every error, with the same requestId in the X-Request-Id header — quote it when you write to us.
The same Idempotency-Key with another body, the first request still running, or a balance that would pass $2,000.
429
RATE_LIMITED
More than 600 requests a minute for this key. Retry-After says when.
500
INTERNAL_ERROR
Something failed on our side. Retry with the same Idempotency-Key; if it persists, write to us with the requestId.
Limits and pages
600 requests a minute per key or panel person; past it, 429 RATE_LIMITED with Retry-After.
Lists answer newest first with ?limit (up to 200); the transaction log pages with ?before=<id>, the event log with ?after=<id>.
Amounts are integer cents (…Minor); per-minute voice rates are decimal dollars.
Webhooks
We POST each event you subscribed to as JSON — id, type, environment, object, data, createdAt. Answer any 2xx. A failed delivery is retried for 24 hours (30 s, 2 min, 10 min, 30 min, then hourly); 50 failures in a row pause the webhook. The address must be public HTTPS; redirects are not followed. Every event also stays in GET /events for 90 days.
X-MirageTel-Event
the event type, e.g. esim.issued
X-MirageTel-Delivery
this delivery’s id (a retry keeps it)
X-MirageTel-Environment
live or test
X-MirageTel-Timestamp
Unix seconds when signed
X-MirageTel-Signature
HMAC-SHA256 hex with your webhook secret over timestamp + "." + raw body
Verify the signature
const crypto = require('crypto');
// rawBody: the request body exactly as received, before any JSON parsing.
function fromMirageTel(headers, rawBody, secret) {
const ts = headers['x-miragetel-timestamp'];
const sig = String(headers['x-miragetel-signature'] || '');
const expected = crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
return fresh && sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
import hashlib, hmac, time
# raw_body: the request body exactly as received (bytes), before any JSON parsing.
def from_miragetel(headers, raw_body: bytes, secret: str) -> bool:
ts = headers.get("X-MirageTel-Timestamp", "")
sig = headers.get("X-MirageTel-Signature", "")
expected = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
fresh = ts.isdigit() and abs(time.time() - int(ts)) < 300
return fresh and hmac.compare_digest(sig, expected)
Event types
Subscribe to a type, a family (esim.*) or everything (*). A payload names no operator and no cost — only what the API itself answers.
The partner account, its services and what this key or person may do
Partner API. Authenticated by a partner key in X-Api-Key (mtp_live_…, mtp_test_…) or by the session of one of the partner's people (the panel) with X-Partner-Environment. Errors are { "error": { "code", "message" }, "requestId" }; every response carries X-Request-Id. 600 requests a minute per key or person. Scope account:read.
Request
No parameters.
Responses
200The account.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
429RATE_LIMITED — more than 600 requests a minute for this key or person. Retry-After says when.
Scope wallet:write. A debit that takes the live balance below lowBalanceAlertMinor sends balance.low (and a mail to your admins and finance people) once per crossing. 0 switches it off. USD 50 by default.
Request
Field
In
Type
lowBalanceAlertMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
body
integer (cents)
required
Responses
200The account.
400INVALID_AMOUNT or NOTHING_TO_CHANGE
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
The prepaid balance — live, or the sandbox's virtual one
Scope wallet:read. A balance holds at most USD 2,000. In the sandbox the balance is virtual.
Request
No parameters.
Responses
200The balance.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
Response fields
environment
string: live, test
balanceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
heldMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
availableMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
capMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
headroomMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
Only in the sandbox (mtp_test_ key or X-Partner-Environment test). Up to USD 2,000. Nothing is money.
Request
Field
In
Type
balanceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
body
integer (cents)
Responses
200The new sandbox balance.
400INVALID_AMOUNT
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
409SANDBOX_ONLY — this is a live key or the live environment
Response fields
balanceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
currency
string
environment
string
Request
curl -X POST "https://miragetel.com/api/v1/partner/sandbox/balance" \
-H "X-Api-Key: mtp_test_…"
Top-ups, receipts and statements
Fund your prepaid balance by card, USDT or bank transfer.
Your top-ups, newest first, with the room left under the cap
Scope wallet:read.
Request
Field
In
Type
limit
query
integer
Responses
200The top-ups.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
Response fields
environment
string
data
array of object
capMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
balanceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
pendingMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
headroomMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
Add funds to the balance — card, USDT or bank transfer
Scope wallet:write. Live: a payment request on your account and what you need to pay it — checkoutUrl (card), usdt (the exact amount to send and our TRON address; the amount identifies your payment), or bank (the details and the reference to quote). Credited when the money arrives, with a receipt in your company's name and a topup.completed event. The balance holds at most USD 2,000 (counting top-ups not yet paid): above it, 409. Sandbox: the play-money balance goes up at once.
Request
Field
In
Type
method
body
string: card, usdt_trc20, bank
required
amountMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
body
integer (cents)
required
Responses
201The top-up and how to pay it.
400INVALID_METHOD, INVALID_AMOUNT or IDEMPOTENCY_KEY_REQUIRED
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
409BALANCE_CAP_EXCEEDED (error.details.headroomMinor says what fits) or an Idempotency-Key conflict
502PAYMENT_PROVIDER_ERROR — the card page could not be opened
503METHOD_UNAVAILABLE or AMOUNT_UNAVAILABLE
Response fields
id
string
reference
string
method
string: card, usdt_trc20, bank
environment
string: live, test
amountMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
currency
string
status
string: pending, completed, rejected, expired
createdAt
string
resolvedAt
string | null
expiresAt
string | null
checkoutUrlCard only
string
usdt
object
bank
object
creditedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
returnedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
404NOT_FOUND
Response fields
id
string
reference
string
method
string: card, usdt_trc20, bank
environment
string: live, test
amountMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
currency
string
status
string: pending, completed, rejected, expired
createdAt
string
resolvedAt
string | null
expiresAt
string | null
checkoutUrlCard only
string
usdt
object
bank
object
creditedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
returnedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
Scope wallet:write. For a USDT top-up whose amount could not be matched (for example a wallet that converted the figure): the transaction is read from the chain and every check of the automatic path applies before anything is credited. What arrived is credited, rounded up to the cent; less than asked is settled by a person.
Request
Field
In
Type
id
path
string
required
txid64 hex characters
body
string
required
Responses
200Credited.
400INVALID_TXID
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
404NOT_FOUND or NOT_FOUND_ON_CHAIN
409REQUEST_NOT_PENDING, ALREADY_CREDITED, TOO_RECENT, AMOUNT_TOO_LOW, NOT_USDT, NOT_SENT_TO_US, USDT_ONLY or LIVE_ONLY
503CHAIN_UNREACHABLE or NO_BUSINESS_ADDRESS
Response fields
id
string
reference
string
method
string: card, usdt_trc20, bank
environment
string: live, test
amountMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
currency
string
status
string: pending, completed, rejected, expired
createdAt
string
resolvedAt
string | null
expiresAt
string | null
checkoutUrlCard only
string
usdt
object
bank
object
creditedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
returnedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
Scope esims:write. Answers at once with the eSIM in processing; it becomes issued (or failed, nothing charged) when the operator answers — usually within seconds — and esim.issued carries the activation code, the iPhone install link and the QR path. reference is your own id for the sale, unique on your account. Needs the eSIM service switched on for your account. Sandbox: issued at once with a test activation code; play money only.
Request
Field
In
Type
packageId
body
integer
required
reference
body
string
activationDateOnly for a plan that needs one.
body
string
Responses
201The eSIM.
400INVALID_PACKAGE, INVALID_REFERENCE, INVALID_ACTIVATION_DATE, ACTIVATION_DATE_REQUIRED or IDEMPOTENCY_KEY_REQUIRED
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
402INSUFFICIENT_BALANCE
403SERVICE_NOT_ACTIVE, PRODUCT_NOT_IN_CATALOGUE, MONTHLY_CAP_REACHED, or a scope or account refusal
404PACKAGE_NOT_FOUND (sandbox)
409REFERENCE_IN_USE, PACKAGE_NOT_AVAILABLE, PRICE_ON_HOLD or an Idempotency-Key conflict
Response fields
idExists from the first answer; follow it to issued or failed.
priceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
currency
string
iccid
string | null
install
object | null
allowanceMb
number | null
usage
object
topupSupported
boolean | null
expiresAt
string | null
failure
string | null
refundedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
priceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
currency
string
iccid
string | null
install
object | null
allowanceMb
number | null
usage
object
topupSupported
boolean | null
expiresAt
string | null
failure
string | null
refundedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
Scope esims:write. Same eSIM, no reinstall. esim.topup.completed (or esim.topup.failed, nothing charged) follows.
Request
Field
In
Type
id
path
string
required
packageCode
body
string
required
Responses
201The top-up.
400INVALID_TOPUP_OPTION or IDEMPOTENCY_KEY_REQUIRED
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
402INSUFFICIENT_FUNDS
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
404NOT_FOUND
409NOT_ISSUED, TOPUP_NOT_SUPPORTED, TOPUP_WINDOW_CLOSED, PRICE_ON_HOLD or ESIM_TEMPORARILY_UNAVAILABLE
503SUPPLY_TEMPORARILY_UNAVAILABLE
Response fields
id
string
esimId
string
environment
string
packageCode
string
priceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
priceMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
currency
string
iccid
string | null
install
object | null
allowanceMb
number | null
usage
object
topupSupported
boolean | null
expiresAt
string | null
failure
string | null
refundedMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
integer (cents)
createdAt
string
Request
curl -X POST "https://miragetel.com/api/v1/partner/esims/<id>/cancel" \
-H "X-Api-Key: mtp_test_…"
Numbers and end users
Local numbers, their registration, and the people who use them.
Buy a phone number for your customer, from your balance
Scope numbers:write. Needs the numbers service switched on for your account and your company’s acceptable use policy. The first month (and a setup fee where the area has one) is paid at once. In a country that registers the user, the number waits for its registration (number.registration.required, then POST …/registration). endUserId names whose line it is; reference is your own id. Sandbox: a +999 number at once, play money.
Request
Field
In
Type
country
body
string
required
areaId
body
string
required
reference
body
string
endUserId
body
string
Responses
201The number.
400INVALID_COUNTRY, INVALID_AREA, INVALID_REFERENCE or IDEMPOTENCY_KEY_REQUIRED
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
402INSUFFICIENT_FUNDS
403SERVICE_NOT_ACTIVE, AUP_REQUIRED, NUMBER_COUNTRY_SANCTIONED, or a scope or account refusal
404COUNTRY_NOT_SOLD or END_USER_NOT_FOUND
409REFERENCE_IN_USE, AREA_NOT_AVAILABLE or an Idempotency-Key conflict
502NUMBER_ORDER_FAILED — nothing was charged
Response fields
id
string
reference
string | null
environment
string: live, test
e164
string | null
country
string
status
string
endUser
object | null
monthlyMinor
object
setupMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
404NOT_FOUND
Response fields
id
string
reference
string | null
environment
string: live, test
e164
string | null
country
string
status
string
endUser
object | null
monthlyMinor
object
setupMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
Scope numbers:write. routing.to: trunk (with trunkId) or none.
Request
Field
In
Type
id
path
string
required
routing
body
object
required
Responses
200The number.
400INVALID_ROUTING
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
404NOT_FOUND
Response fields
id
string
reference
string | null
environment
string: live, test
e164
string | null
country
string
status
string
endUser
object | null
monthlyMinor
object
setupMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
404NOT_FOUND
409NOT_CANCELLABLE
Response fields
id
string
reference
string | null
environment
string: live, test
e164
string | null
country
string
status
string
endUser
object | null
monthlyMinor
object
setupMinorAn amount in minor units (cents), stored as PostgreSQL BIGINT. Never a float, never a decimal string, never scaled at the boundary. 1999 means $19.99. Signed where the field represents a movement (ledger entries, adjustments); non-negative where it represents a balance.
Scope numbers:write. identityType, identity and address fields as the form asks — filled from the end user you gave the number when you leave them out — and documents (base64). The verdict arrives as number.registration.approved or …rejected (with what to correct).
Request
Field
In
Type
id
path
string
required
identityType
body
string: personal, business
identity
body
object
address
body
object
serviceDescription
body
string
documents
body
array of object
Responses
200Filed.
400A field or a document is missing or invalid
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
404NOT_FOUND
409NO_REGISTRATION or LIVE_ONLY
Request
curl -X POST "https://miragetel.com/api/v1/partner/numbers/<id>/registration" \
-H "X-Api-Key: mtp_test_…"
GET/end-usersend_users:read
Your end users
Scope end_users:read. What a registration needs is held encrypted and never read back in full.
Request
Field
In
Type
reference
query
string
limit
query
integer
Responses
200The end users.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
Connect a phone system — the password is shown once
Scope trunks:write. Needs outbound calling switched on for your account and acceptAup: true with the current aupVersion. Minutes are paid from your balance at your rate card (GET /partner/rates/voice); maxChannels and dailyLimitMinor cap it.
Request
Field
In
Type
name
body
string
maxChannels
body
integer
dailyLimitMinor
body
object
cliMode
body
string: fixed, random
cliNumberId
body
string
acceptAup
body
boolean
required
aupVersion
body
string
required
Responses
201The trunk and its password.
400AUP_NOT_ACCEPTED, INVALID_NAME, INVALID_CHANNELS, INVALID_DAILY_LIMIT or CLI_REQUIRED
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403SERVICE_NOT_ACTIVE, or a scope, account or compliance refusal
409NO_NUMBER, NO_CREDIT, AUP_VERSION_CHANGED or an Idempotency-Key conflict
Scope sms:write. Needs outbound texts switched on for your account. Charged per part at your SMS rate card; text.status.changed follows the operator’s verdict.
Request
Field
In
Type
numberId
body
string
required
to
body
string
required
text
body
string
required
Responses
201Sent.
400INVALID_MESSAGE or INVALID_NUMBER
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
402INSUFFICIENT_FUNDS
403SERVICE_NOT_ACTIVE, DESTINATION_NOT_ALLOWED or DESTINATION_SANCTIONED
409NO_ACTIVE_NUMBER, SMS_SENDING_NOT_OPEN or SMS_DESTINATION_NOT_PRICED
Scope rates:read. Your price from your partner agreement, in whole cents — the price a purchase charges. available is false while a package cannot be bought this minute (for example out of stock), with the reason in hold. format=csv downloads the same list.
Request
Field
In
Type
country
query
string
format
query
string: json, csv
Responses
200The packages.
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
Scope webhooks:manage. A public https address (resolved now and before every delivery; private addresses are refused). events: exact types or families (esim.*), or *. Each delivery is signed with HMAC-SHA256 over "<X-MirageTel-Timestamp>.<body>" in X-MirageTel-Signature and retried for 24 hours.
Request
Field
In
Type
url
body
string
required
events
body
array of string
description
body
string
Responses
201Created
400INVALID_WEBHOOK_URL, WEBHOOK_NOT_PUBLIC, WEBHOOK_UNRESOLVED, UNKNOWN_EVENT or IDEMPOTENCY_KEY_REQUIRED
401API_KEY_INVALID, or no key and no session (UNAUTHORIZED).
403INSUFFICIENT_SCOPE, PANEL_ONLY, PARTNER_ONLY, PARTNER_SUSPENDED (reads only) or PARTNER_INACTIVE.
409TOO_MANY_WEBHOOKS or an Idempotency-Key conflict
Response fields
id
string
environment
string: live, test
url
string
events
array of string
description
string | null
active
boolean
failuresInRow
integer
lastSuccessAt
string | null
lastFailureAt
string | null
lastError
string | null
disabledReason
string | null
secretOnly when the webhook is created or its secret rotated.
Once your partner account is active, an administrator issues keys in the partner panel under Developers. A developer of your team can issue sandbox keys.
What is the sandbox?
A second environment with its own keys (mtp_test_…), its own play-money balance, its own objects and its own events. Nothing in it reaches an operator or your real balance.
Can a request be charged twice?
No, if you send an Idempotency-Key: a retry with the same key and the same body gets the first answer back. Every request that creates or pays requires one.
How do I know a webhook is from you?
Check X-MirageTel-Signature: HMAC-SHA256 with your webhook secret over the X-MirageTel-Timestamp, a dot and the raw body. Refuse an old timestamp.
Is there an OpenAPI file?
Yes — /partners/developers/openapi.json, the same contract this page is generated from. Point your client generator at it.
Own Signal
Keys open with your account.
Apply, we review your company, and your sandbox is ready the day we activate you.