Everything a client can do in the panel — order and pay for servers, power and rebuild them, read credentials, handle invoices,
the wallet and support tickets — over one JSON REST API.
Connect your billing system, keep the wallet funded, and every order you place starts building straight away.
export API_KEY="mlk_…" # from API access in the panel
BASE="https://my.matrixstreamline.com/api/v1"
AUTH="Authorization: Bearer $API_KEY"
# 1 · What can I sell?
curl "$BASE/plans" -H "$AUTH"
# 2 · Installable OS, free IPs and the exact price
curl "$BASE/order-options?plan_id=2&zone_id=1" -H "$AUTH"
# 3 · Order and pay from the wallet in one call
curl -X POST "$BASE/orders" -H "$AUTH" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"plan_id": 2, "zone_id": 1, "os_id": 2,
"hostname": "web-01.example.in", "pay_with": "wallet"}'
# 4 · Poll until "status" is "completed"; vm_id is your server
curl "$BASE/orders/311" -H "$AUTH"
Keys look like mlk_ followed by 48 hexadecimal characters. The panel stores only a SHA-256 fingerprint, so a lost key cannot be shown again — revoke it and create a new one.
If a proxy in your stack strips Authorization, send the key as X-API-Key: mlk_… instead.
A key acts on the account that created it, with the same rights the account holder has in the client panel. Up to 20 active keys per account.
Keys can carry an expiry date. Revoking a key (in the panel or with DELETE /api-keys/{id}) takes effect immediately.
The administrator can switch API access off for an account, or for the whole panel.
401 — missing or wrong key
{
"error": {
"code": "invalid_api_key",
"message": "That API key is not valid or has been revoked.",
"request_id": "req_9f2c4b1a7d3e5f60"
}
}
Each key has its own whitelist. A request is accepted only when it comes from an address on the list, so a leaked key is useless anywhere else.
Entries are single addresses or CIDR ranges, IPv4 or IPv6: 203.0.113.10, 198.51.100.0/24, 2001:db8:42::/48. Up to 50 per key.
Catch-all ranges (0.0.0.0/0, ::/0) are refused.
This panel requires a whitelist: a key with no entries is refused (ip_whitelist_empty).
Manage the list in the panel, or through the API: read, replace,
add, remove. Changing the calling key's list so that it would block
the address you call from is refused with would_lock_out unless you send "force": true.
The address checked is the one the panel sees. Calling through your own proxy or NAT? Whitelist its public address. GET /ping echoes the address the panel saw.
403 — address not on the list
{
"error": {
"code": "ip_not_whitelisted",
"message": "Requests from 192.0.2.77 are not allowed for this key. Add the address to the key's IP whitelist.",
"request_id": "req_51c0d2e3f4a5b6c7"
}
}
Send bodies as JSON with Content-Type: application/json (form-encoded bodies are also accepted). Query parameters go in the URL.
Every response is JSON. A single object comes back in data; a list comes back in data with paging in meta.
Money is a number with two decimals in the account currency (INR). Prices exclude tax unless named total.
Timestamps are ISO 8601 in UTC (2026-10-10T09:12:44Z); dates are YYYY-MM-DD. Absent values are null, never omitted.
Servers are identified by a UUID; orders, invoices and tickets by a number id, or by their reference (ORD-…, MTX-…, TKT-…) wherever an id goes in the path.
Every response has an X-Request-Id header; errors repeat it as request_id. Quote it when you contact support.
Failures use the HTTP status and a stable code to branch on, with a message written for people. Validation errors add fields, mapping each field to its problem.
422 — validation failed
{
"error": {
"code": "validation_failed",
"message": "Plan id is required.",
"request_id": "req_0a1b2c3d4e5f6a7b",
"fields": {
"plan_id": "Plan id is required.",
"hostnames": "Send \"hostname\" for one server, or \"hostnames\" (a list) for several."
}
}
}
Status
Code
Meaning
400
invalid_json
The body was sent as application/json but did not parse.
400
invalid_idempotency_key
The Idempotency-Key header is longer than 100 characters or not printable ASCII.
401
missing_api_key
No key in the Authorization (or X-API-Key) header.
401
invalid_api_key
The key does not exist or was revoked.
401
api_key_expired
The key passed its expiry date.
402
insufficient_balance
The wallet does not cover a wallet payment.
403
ip_not_whitelisted
The calling IP is not on the key's whitelist. The message names the IP the panel saw.
403
ip_whitelist_empty
The key has no whitelist entries and the panel requires them.
403
read_only_key
A read-only key made a non-GET request.
403
api_access_disabled
API access is turned off for this account.
403
account_inactive
The account is suspended or closed.
404
not_found
No such endpoint, or the resource does not exist on your account.
405
method_not_allowed
The path exists but not with this HTTP method.
409
idempotency_in_progress
A request with the same Idempotency-Key is still running.
Networks fail. To retry a write safely, send a unique Idempotency-Key header (a UUID is ideal) with every POST, PUT, PATCH or DELETE that must not happen twice — above all placing orders and paying invoices.
The first request runs normally and its response is stored for 24 hours against the key.
A retry with the same key, path and body gets the stored response back, with the header Idempotent-Replayed: true. Nothing runs twice.
A retry while the first is still running gets 409 idempotency_in_progress; wait and retry.
Reusing a key for a different request gets 422 idempotency_key_reused.
Server errors (5xx) are not stored, so the retry runs for real.
The Postman collection sends a fresh Idempotency-Key with every order, invoice payment and top-up.
Order and pay in one call.POST /orders with "pay_with": "wallet" and an Idempotency-Key.
The response holds the orders, the paid invoice and your new wallet balance. Have a discount code? Check it with
GET /coupons/{code} and send it as "coupon".
Wait for the build. Poll GET /orders/{id} every 15–30 seconds until status is
completed (usually 1–5 minutes for Linux, up to 20 for Windows); vm_id then holds the server id. failed means the build failed —
open a ticket; the order can be re-provisioned by staff.
Hand over.GET /vms/{id}/credentials gives the IP, username and password for your customer.
Store your customer reference in the server's label.
Renewals. Each server renews monthly. A renewal invoice is raised a few days before billing.next_due_date; list unpaid ones with
GET /invoices?status=unpaid and pay each with POST /invoices/{id}/pay ("method": "wallet")
by the due date. The wallet is never charged without that call. GET /wallet shows what is due in the next 30 days.
Ending a server. Set auto_renew to false with PATCH /vms/{id} so no further renewal invoice is raised,
then open a ticket to have it terminated; servers are not removed automatically. Unpaid servers are suspended a few days after the due date (status suspended) and later terminated; paying the invoice lifts a suspension.
Card and UPI payments (wallet top-ups, or paying an invoice by gateway) are completed by the account holder in the panel through the returned
payment_url. Fully automated resellers pay everything from the wallet.
When something goes wrong, GET /api-logs shows every call your keys made — including refused ones and the reason —
and GET /vm-actions shows what happened on your servers.
Change profile and billing details. Send only the fields to change; an empty string clears an optional field. Email and password can only be changed in the panel.
The audit trail of the account, newest first: sign-ins, profile changes, orders, payments, rebuilds, key changes. "via" tells whether the action came from the panel or the API.
Query parameters
Name
Type
Description
actionoptional
string
Only this action, e.g. login, order_placed, invoice_paid_wallet, vm_rebuild, api_key_revoked.
viaoptional
string
"api" or "panel".
searchoptional
string
Text in the description.
fromoptional
date
From this day (UTC), YYYY-MM-DD.
tooptional
date
Up to and including this day (UTC).
pageoptional
integer
Page number, from 1. Pages past the end return an empty list.
Keys are created in the panel under Account → API access, where the full key is shown once. Through the API you can list keys, manage the IP whitelist of each key and revoke keys — enough to rotate keys and follow your servers' addresses without signing in.
PUT/api/v1/api-keys/{id}/whitelistKey access: full
Sets the whole list. Entries are single IPv4/IPv6 addresses or CIDR ranges; they are stored in canonical form (198.51.100.7/24 becomes 198.51.100.0/24).
Path parameters
Name
Type
Description
idrequired
integer
Key id.
Body parameters
Name
Type
Description
ipsrequired
string[]
The new list, at most 50 entries. Must not be empty while the panel requires whitelists.
forceoptional
boolean
Apply even when the new list would block the address you are calling from (only checked when changing the calling key).
Everything needed to build a valid order for a plan in a zone: the installable operating systems, the host nodes, the free IP pools, and the exact price for the quantity — computed the way the invoice will be.
ip_pools groups the free addresses by their first three parts; order in one with "ip_pool" on POST /orders and each server gets the next free address in it. ips (every free address) is deprecated and kept only so existing integrations keep working; use ip_pools. "available": false means the plan is not sold in that zone. total is what the order invoice will charge; tax is worked out after the discount. renewal_monthly is what the servers renew at each month, before tax (lower with a recurring coupon).
Errors specific to this endpoint
Status
Code
When
404
not_found
The plan does not exist or is not active.
422
coupon_invalid
A coupon was given and it does not fit this order.
422
validation_failed
plan_id or zone_id is missing, the zone is not taking orders, or quantity is out of range.
Discount codes created by the panel administrator. A percentage coupon takes that share off each server's monthly price and, on the first invoice, its setup fee; a fixed coupon takes an amount off each server's monthly price. Recurring coupons keep the monthly discount on every renewal. Discounts come off before tax. Each order placed with a code is one use.
Tells whether a code can be used and what it takes off. With plan_id, every rule is checked for that plan and your account (dates, use limits, plan list, first-order rule) and the exact price is returned. Checking does not use up the code.
Without plan_id only the code itself is checked (exists and enabled); discount, pricing and valid_for_plan are then left out / null. plan_ids is empty when the code works on every plan. valid_until is the last day the code works.
Errors specific to this endpoint
Status
Code
When
404
not_found
plan_id was given but is not an active plan.
422
coupon_invalid
The code is unknown, disabled, not yet valid, expired, used up, not for this plan, already used by you, or for first orders only. The message says which.
An order buys 1–10 servers of one plan in one zone. Each server becomes its own order; they share one invoice. Paying the invoice builds every server automatically.
Creates the orders and their invoice. With "pay_with": "wallet" the invoice is paid from the wallet in the same call and the servers start building at once; the wallet is checked first, so a short balance creates nothing. Without it the invoice waits for POST /invoices/{id}/pay.
Body parameters
Name
Type
Description
plan_idrequired
integer
Plan to order (GET /plans).
zone_idrequired
integer
Zone to deploy in (GET /zones).
os_idrequired
integer
Operating system; must be allowed by the plan and installable in the zone (GET /order-options).
hostnameoptional
string
Hostname for a single server. Letters, digits, hyphens and dots.
hostnamesoptional
string[]
One hostname per server for 1–10 servers, all different. Use this or hostname.
ip_pooloptional
string
An IP pool from GET /order-options, e.g. "161.248.163". Each server gets the next free address in it; without it the address is picked automatically.
ip_idsoptional
integer[]
Deprecated — exact IP ids from the ips list, at most one per server. Use ip_pool; do not send both.
server_idoptional
integer
Pin all servers to one host node in the zone.
couponoptional
string
A coupon code (not case-sensitive). The discount comes off before tax; check a code first with GET /coupons/{code}.
pay_withoptional
string
"wallet" to pay immediately from the wallet, or "none" (default) to leave the invoice unpaid.
Send an Idempotency-Key header so a retried request can never place a second order. payment.status is "unpaid" without pay_with, "paid" on success, or "failed" (with payment.error) if the wallet changed between the check and the charge — the order then waits for payment. Poll GET /orders/{id} until status is completed; vm_id then holds the server id.
Errors specific to this endpoint
Status
Code
When
403
read_only_key
The key is read-only.
402
insufficient_balance
pay_with is wallet and the balance does not cover the total. Nothing was created.
422
coupon_invalid
The coupon is unknown, disabled, expired, used up, not for this plan, or for first orders only. The message says which. Nothing was created.
422
ip_pool_exhausted
The chosen ip_pool has fewer free addresses than servers ordered. Nothing was created.
422
order_rejected
The order is not possible: sold out, OS not on the plan, zone closed, no capacity, IP taken, account server limit, invalid or duplicate hostnames. The message says which.
Virtual servers on the account. A server's id is a UUID. Statuses: pending, provisioning, active, rebuilding, suspended, failed, terminated. power_state is running, stopped or unknown.
Full detail: IP addresses with gateway and netmask, login username and this month's bandwidth. The password is only returned by GET /vms/{id}/credentials.
error_message is set only when status is failed. traffic.limit_bytes is null for unmetered plans. rebuild is "queued" or "running" while a rebuild job exists for the server or the server is rebuilding, and null otherwise; status stays "active" while the job is queued, so watch rebuild, not status.
Set a private label (for example your customer's reference) and switch auto-renew. With auto-renew off, no renewal invoice is raised and the server ends after its paid period.
Erases the disk and installs the chosen operating system. The id, hostname, IP and billing stay the same. The rebuild is queued: status stays "active" until the job starts, then shows "rebuilding", then "active" again, so poll GET /vms/{id} until its rebuild field is null rather than watching status. A new password is generated unless you send one.
Path parameters
Name
Type
Description
idrequired
uuid
The server's id (a UUID) from GET /vms.
Body parameters
Name
Type
Description
os_idrequired
integer
Target OS, from GET /vms/{id}/os-options.
confirmrequired
string
The server's exact hostname, as a safeguard — all data is lost.
passwordoptional
string
Root/Administrator password for the new install, 8–72 characters. Generated when omitted.
{
"data": {
"id": "6f1c2a9e-3b7d-4c8a-9e21-5d4f3a2b1c0d",
"queued": true,
"os_id": 4,
"password": "generated",
"message": "Rebuild queued. Status stays \"active\" until the job starts, so poll GET /vms/{id} until \"rebuild\" is null. Then GET /vms/{id}/actions shows the outcome; read the new login from GET /vms/{id}/credentials only after a rebuild entry with status \"success\"."
}
}
The account owner is also emailed the new login when the rebuild finishes. Once rebuild is null, the newest rebuild entry in GET /vms/{id}/actions tells you how it ended: success (read /credentials), failed (the server returns to its previous status; the message says why), or still the "pending" entry from queueing, which means the job was refused before it touched the server (for example because the server was suspended meanwhile) and the old install and login are unchanged.
Errors specific to this endpoint
Status
Code
When
403
read_only_key
The key is read-only.
404
not_found
No server with that id on your account.
409
rebuild_in_progress
A rebuild of this server is already queued.
409
vm_suspended
The server is suspended (also vm_busy, vm_terminated, vm_failed as for power actions).
422
validation_failed
confirm does not match the hostname, the OS is not allowed, or the password length is wrong.
Sets the server's IP address, gateway and DNS inside it again, the way the panel set them up, through the guest agent. Use it when the network inside the server was changed or stopped working. The hostname and disk are left alone; the connection drops for a few seconds. The server must be running.
Path parameters
Name
Type
Description
idrequired
uuid
The server's id (a UUID) from GET /vms.
Body
No body.
Example
Request · curl
curl -X POST "https://my.matrixstreamline.com/api/v1/vms/6f1c2a9e-3b7d-4c8a-9e21-5d4f3a2b1c0d/network" \
-H "Authorization: Bearer $API_KEY"
Response202
{
"data": {
"id": "6f1c2a9e-3b7d-4c8a-9e21-5d4f3a2b1c0d",
"queued": true,
"message": "Reconfigure IP queued. It takes under a minute on Linux, a little longer on Windows; GET /vms/{id}/actions shows a \"network\" entry with status \"success\" or \"failed\" when it is done."
}
}
The job runs in the background. Poll GET /vms/{id}/actions for the "network" entry; a failed one explains why (for example a stopped server or no guest agent).
Errors specific to this endpoint
Status
Code
When
403
read_only_key
The key is read-only.
404
not_found
No server with that id on your account.
409
vm_busy
The server is not active.
409
vm_suspended
The server is suspended.
409
rebuild_pending
A rebuild is queued for the server.
409
network_pending
A Reconfigure IP of this server is already queued.
Creates a one-time console session for embedding noVNC in your own panel: connect noVNC to websocket_url and use vnc_password as the VNC password. Valid once, for 120 seconds.
Path parameters
Name
Type
Description
idrequired
uuid
The server's id (a UUID) from GET /vms.
Body
No body.
Example
Request · curl
curl -X POST "https://my.matrixstreamline.com/api/v1/vms/6f1c2a9e-3b7d-4c8a-9e21-5d4f3a2b1c0d/console" \
-H "Authorization: Bearer $API_KEY"
Invoice types: new (an order), renewal (a server's next month), topup (wallet deposit, no tax) and manual. Statuses: unpaid, paid, cancelled, refunded. Renewal invoices are raised automatically before the due date; pay them with POST /invoices/{id}/pay — the wallet is not charged automatically.
"wallet" settles the invoice at once from the wallet balance and runs its effects (builds ordered servers, extends a renewal, lifts a suspension). A gateway ("razorpay" or "cashfree") returns a payment_url for the account holder to complete in a browser signed in to the panel; the invoice is marked paid automatically when the gateway confirms.
Path parameters
Name
Type
Description
idrequired
integer|string
Invoice id or number.
Body parameters
Name
Type
Description
methodrequired
string
"wallet", or an enabled gateway name (see GET /wallet → topup.gateways).
Gateway response: {"data": {"invoice": {…}, "payment": {"method": "razorpay", "status": "pending", "payment_url": "https://panel.example.com/billing/checkout/915/razorpay", "message": "…"}}}. Send an Idempotency-Key when paying so a retry cannot pay twice (a repeat wallet payment of a paid invoice is refused with already_paid anyway).
A prepaid balance. Resellers usually keep it funded and order with "pay_with": "wallet", so servers are built without any manual step. Every movement is recorded in a double-entry ledger.
Raises a top-up invoice (a deposit, so no tax) and returns the payment_url where the account holder pays by card, UPI or net banking in a browser signed in to the panel. The wallet is credited when the gateway confirms.
Body parameters
Name
Type
Description
amountrequired
number
Amount to add, within the min/max from GET /wallet.
curl -X POST "https://my.matrixstreamline.com/api/v1/tickets" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject": "Reverse DNS for 103.20.10.11",
"department": "support",
"priority": "medium",
"vm_id": "6f1c2a9e-3b7d-4c8a-9e21-5d4f3a2b1c0d",
"message": "Please set the PTR record of 103.20.10.11 to web-01.client1042.in."
}'
Response201
{
"data": {
"id": 77,
"ticket_number": "TKT-9C1E44AB",
"subject": "Reverse DNS for 103.20.10.11",
"department": "support",
"priority": "medium",
"status": "open",
"vm": {
"id": "6f1c2a9e-3b7d-4c8a-9e21-5d4f3a2b1c0d",
"hostname": "web-01.client1042.in"
},
"reply_count": 1,
"last_reply_at": "2026-10-10T10:02:13Z",
"created_at": "2026-10-10T10:02:13Z",
"updated_at": "2026-10-10T10:02:13Z",
"replies": [
{
"id": 301,
"from": "client",
"author": "Asha Rao",
"message": "Please set the PTR record of 103.20.10.11 to web-01.client1042.in.",
"created_at": "2026-10-10T10:02:13Z"
}
]
}
}
Errors specific to this endpoint
Status
Code
When
403
read_only_key
The key is read-only.
422
validation_failed
A field is missing or invalid, or vm_id is not your server.
curl -X POST "https://my.matrixstreamline.com/api/v1/tickets/77/replies" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Thanks! Please do the same for web-02."
}'
Response201
{
"data": {
"id": 77,
"ticket_number": "TKT-9C1E44AB",
"subject": "Reverse DNS for 103.20.10.11",
"department": "support",
"priority": "medium",
"status": "customer_reply",
"vm": {
"id": "6f1c2a9e-3b7d-4c8a-9e21-5d4f3a2b1c0d",
"hostname": "web-01.client1042.in"
},
"reply_count": 3,
"last_reply_at": "2026-10-10T10:02:13Z",
"created_at": "2026-10-10T10:02:13Z",
"updated_at": "2026-10-10T10:02:13Z",
"replies": [
{
"id": 301,
"from": "client",
"author": "Asha Rao",
"message": "Please set the PTR record of 103.20.10.11 to web-01.client1042.in.",
"created_at": "2026-10-10T10:02:13Z"
}
]
}
}
The response is the full ticket (as GET /tickets/{id}) including your new reply.
Your own logs, for audits and debugging integrations. API calls are kept 30 days; account activity and server actions 180 days. The account audit trail is GET /account/activity.
Every request made with this account's keys, newest first — including refused ones (wrong IP, read-only key, rate limit) so you can see why an integration failed.
Query parameters
Name
Type
Description
key_idoptional
integer
Only calls made with this key.
methodoptional
string
GET, POST, PUT, PATCH or DELETE.
statusoptional
string
An exact status such as 403, or a class: 2xx, 4xx, 5xx.
errors_onlyoptional
boolean
Only responses 400 and above.
pathoptional
string
Text in the path, e.g. /orders.
fromoptional
date
From this day (UTC), YYYY-MM-DD.
tooptional
date
Up to and including this day (UTC).
pageoptional
integer
Page number, from 1. Pages past the end return an empty list.
Power actions, builds, rebuilds, password resets, console sessions, suspensions and terminations on all your servers, newest first. For one server you can also use GET /vms/{id}/actions.