Reseller API
Connect a Dhru Fusion panel, or call the REST API from your own code. Both use the same API key and charge your wallet at your reseller prices. Keys are for reseller accounts and each one works from a single server IP.
What you can order
This store sells IMEI and server services through the API. The same API key works for every kind; a kind the store doesn't sell is refused and its list is empty.
| Kind | List the services | Place an order |
|---|---|---|
| IMEI | Dhru imeiservicelist; REST GET /services → imei | placeimeiorder; POST /orders |
| Server | Dhru serverservicelist; REST GET /services → server | placeserverorder (or placeimeiorder); POST /orders |
Every rate limit, order cap and timeout this store enforces is on the Limits page.
Quickstart
Add imeifixer to your Dhru Fusion panel as an API supplier (type: Dhru Fusion). You need three values:
- 1
API URL
https://imeifixer.com/api/index.php - 2
Username
The email address you sign in to imeifixer with.
- 3
API key
Create one under Account → API keys, bound to the public IP of the server your panel runs on. API keys need a reseller account.
Then use your panel's connection test or service sync: it calls accountinfo and imeiservicelist (and serverservicelist for server services).
Endpoint
The gateway answers on any of these URLs; they are the same endpoint, so use whichever your panel expects:
https://imeifixer.com/api/index.phphttps://imeifixer.com/api/v1/serviceshttps://imeifixer.com/api/v1/services/api/index.php
Requests are POST, form-encoded, with the fields username, apiaccesskey, action and, for actions that take input, parameters: the base64 of a JSON object (base64 XML <PARAMETERS>…</PARAMETERS> is accepted too). For single-order actions, top-level ID, IMEI and Qnt fields fill in anything parameters leaves out. A GET answers 405.
Actions
| action | parameters | Returns in data |
|---|---|---|
| accountinfo | none | credit, mail, currency |
| imeiservicelist | none | Services grouped by GROUPNAME: SERVICEID, SERVICENAME, CREDIT (your price), TIME, QNT, Requires (extra fields) |
| serverservicelist | none | Server services and remote services (SERVICETYPE REMOTE), in the same GROUPNAME / SERVICES shape as imeiservicelist |
| placeimeiorder | ID, IMEI, Qnt, plus any service fields | REFERENCEID, MESSAGE |
| placeimeiorderbulk | { your ref: { ID, IMEI, Qnt, … } }, up to 200 lines | Per line SUCCESS or ERROR; each line is charged on its own |
| getimeiorder | ID = the REFERENCEID | STATUS, CODE |
| getimeiorderbulk | { your ref: REFERENCEID } | Per line STATUS and CODE, or ERROR |
| placeserverorder, placeserverorderbulk | as the IMEI versions; IMEI carries the server value (username, email, …) | as the IMEI versions |
| getserverorder, getserverorderbulk | as the IMEI versions | as the IMEI versions |
Unknown fields in a placement are ignored, so panel extras do no harm.
Server and remote services work like IMEI ones: sync them with serverservicelist, place them with placeserverorder (or placeimeiorder; both accept any SERVICEID) and poll with getserverorder. Put the value the service needs (a username, an email, a serial) in IMEI; if you leave IMEI out, the service's first field from Requires is used. Dhru has no list action for remote services, so serverservicelist lists them too, tagged SERVICETYPE REMOTE (see Service types). A service's extra fields go in CUSTOMFIELD (see Services with extra fields).
First call: accountinfo
curl https://imeifixer.com/api/index.php \ --data-urlencode username=you@example.com \ --data-urlencode apiaccesskey=imf_your_key \ --data-urlencode action=accountinfo
{
"SUCCESS": [
{
"message": "",
"data": {
"credit": "20.0000",
"mail": "you@example.com",
"currency": "USD"
}
}
],
"apiversion": "6.1"
}Run it from the server whose IP the key is bound to. From anywhere else you get the invalid credentials error.
Place an order
Build parameters by base64-encoding the JSON. On Linux, add -w0 to base64 for long values so it doesn't wrap lines.
PARAMS=$(printf '%s' '{"ID":"12","IMEI":"354800000012950","Qnt":1}' | base64)
curl https://imeifixer.com/api/index.php \
--data-urlencode username=you@example.com \
--data-urlencode apiaccesskey=imf_your_key \
--data-urlencode action=placeimeiorder \
--data-urlencode "parameters=$PARAMS"PARAMS comes out as eyJJRCI6IjEyIiwiSU1FSSI6IjM1NDgwMDAwMDAxMjk1MCIsIlFudCI6MX0=. Always send it with --data-urlencode (or your HTTP library's form encoding): base64 contains + and /.
{
"SUCCESS": [
{
"message": "",
"data": {
"REFERENCEID": "IF1",
"MESSAGE": "Order placed."
}
}
],
"apiversion": "6.1"
}Store REFERENCEID; it is how you ask for the result. The order is charged when it is placed.
Get the result
PARAMS=$(printf '%s' '{"ID":"IF1"}' | base64)
curl https://imeifixer.com/api/index.php \
--data-urlencode username=you@example.com \
--data-urlencode apiaccesskey=imf_your_key \
--data-urlencode action=getimeiorder \
--data-urlencode "parameters=$PARAMS"{
"SUCCESS": [
{
"message": "",
"data": {
"STATUS": "1",
"CODE": ""
}
}
],
"apiversion": "6.1"
}{
"SUCCESS": [
{
"message": "",
"data": {
"STATUS": "4",
"CODE": "Model: iPhone 13\nIMEI: 354800000012950\nFind My: OFF\nBlacklist: Clean"
}
}
],
"apiversion": "6.1"
}We check with the supplier about every 60 seconds, so polling an order more often than that only uses up your limit.
STATUS values
| STATUS | Meaning | Details |
|---|---|---|
| 0 | New | Paid and queued for the supplier. |
| 1 | In process | Accepted by the supplier; we keep checking it. |
| 3 | Rejected | Rejected, or refunded because it could not be done. The charge is back in your balance automatically. |
| 4 | Success | Done. The result is in CODE. |
These are the Dhru Fusion codes, so a stock panel maps them without changes: 4 is success and 3 is rejected. If your panel was set up with the two swapped, put them back to the Dhru default.
Bulk
placeimeiorderbulk takes an object keyed by your own line references (up to 200 lines). Each line is checked and charged on its own, so one bad line doesn't stop the others.
{
"a1": {
"ID": "12",
"IMEI": "354800000012950",
"Qnt": 1
},
"a2": {
"ID": "12",
"IMEI": "35480000001"
}
}{
"a1": {
"SUCCESS": [
{
"REFERENCEID": "IF2",
"MESSAGE": "Order placed."
}
]
},
"a2": {
"ERROR": [
{
"message": "invalid IMEI"
}
]
}
}getimeiorderbulk works the same way: send {"a1": "IF2", …} and get STATUS and CODE per reference.
Responses
Every response is HTTP 200 with "apiversion": "6.1". Check for a SUCCESS or an ERROR key, not the HTTP status.
{
"SUCCESS": [
{
"message": "",
"data": {
"REFERENCEID": "IF1",
"MESSAGE": "Order placed."
}
}
],
"apiversion": "6.1"
}{
"ERROR": [
{
"message": "Invalid credentials: check username (your login email), API key, and that this server's IP matches the key's bound IP."
}
],
"apiversion": "6.1"
}IP binding and lockout
- Each key works only from the IP you bound it to. Behind NAT or a proxy that is your server's public (outgoing) address. To move servers, create a key for the new IP and revoke the old one. Don't know the IP? Create a key that binds to the first server that calls.
- After 10 failed checks (wrong username, key or IP) from one IP within 15 minutes, that IP is refused, even with the right key, until the time runs out. The error says how many minutes are left.
- Status calls (
getimeiorder,getserverorderand their bulk versions) are limited to 60 per 60 seconds per login and IP. Use the bulk version to check many orders at once. - Recent calls on each key shows what we received and what we answered. A call that fails the credential check can't be tied to a key, so it isn't listed there.
Every limit in one place: the Limits page.
Callbacks (push instead of polling)
Set a callback URL on an API key in API keys and we POST each order placed with that key to it as soon as the order is completed, rejected or refunded. You can keep polling as a fallback; the callback just tells you sooner.
- The URL must be
https://on the standard port, with a public host name. We don't follow redirects. - Answer with any
2xxwithin 10 seconds. Anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h and 6 h; then we give up. The key's page lists the last 20 deliveries and what your server answered. - After we give up, the delivery shows Gave up on the key's page. Nothing is lost:
getimeiorder(orGET /orders/<reference_id>on REST) still returns the result, so poll it. Once your endpoint works again, ask support to resend the callback and quote theREFERENCEID. - A delivery can arrive twice (for example after a timeout on your side). Use
reference_id(or theX-Imeifixer-Deliveryheader) to ignore repeats. statusis the Dhru STATUS value:4completed,3rejected or refunded.codeis only present for a completed order.
POST /your/callback/path HTTP/1.1 Content-Type: application/json User-Agent: imeifixer-callbacks/1 X-Imeifixer-Delivery: 8812 X-Imeifixer-Signature: t=1791109267,v1=5f1c0b0e…(64 hex characters)
{
"event": "order.finished",
"reference_id": "IF10452",
"status": "4",
"status_text": "completed",
"code": "Model: iPhone 13\nIMEI: 354800000012950\nFind My: OFF",
"completed_at": "2026-10-04T10:21:07.512918+00:00"
}| Field | Meaning |
|---|---|
| event | Always order.finished (a test from the key page sends ping). |
| reference_id | The REFERENCEID you got when you placed the order. |
| status | Dhru STATUS: 4 completed, 3 rejected/refunded. |
| status_text | completed, rejected or refunded. |
| code | The result (unlock code, check report). Completed orders only. |
| completed_at | When the order finished (ISO 8601, UTC). |
Check the signature before trusting the body. X-Imeifixer-Signature is t=<unix seconds>,v1=<hex>, where the hex is HMAC-SHA256 of <t>.<raw body> keyed with the key's signing secret. Use the raw bytes you received, not re-encoded JSON, and refuse timestamps more than 5 minutes old.
import hashlib, hmac, time
SECRET = "whsec_..." # shown once when you set the callback URL
def verify(raw_body: bytes, header: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
timestamp = int(parts.get("t", "0"))
if abs(time.time() - timestamp) > tolerance:
return False # too old: a replay
signed = f"{timestamp}.".encode() + raw_body
expected = hmac.new(SECRET.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))import crypto from "node:crypto";
const SECRET = process.env.IMEIFIXER_CALLBACK_SECRET; // "whsec_..."
export function verify(rawBody /* Buffer */, header, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const timestamp = Number(parts.t);
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = crypto
.createHmac("sha256", SECRET)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
const given = Buffer.from(parts.v1 ?? "", "utf8");
return given.length === expected.length && crypto.timingSafeEqual(given, Buffer.from(expected));
}<?php
$secret = 'whsec_...';
$raw = file_get_contents('php://input'); // the raw body, before json_decode
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_IMEIFIXER_SIGNATURE'] ?? ''), $sig);
$ok = isset($sig['t'], $sig['v1'])
&& abs(time() - (int) $sig['t']) <= 300
&& hash_equals(hash_hmac('sha256', $sig['t'] . '.' . $raw, $secret), $sig['v1']);
if (!$ok) { http_response_code(400); exit; }
$event = json_decode($raw, true);
http_response_code(200);Error messages
| message | What it means |
|---|---|
Invalid credentials: check username (your login email), API key, and that this server's IP matches the key's bound IP. | Username, key or calling IP is wrong. We don't say which, on purpose. |
Too many failed attempts from this IP; try again in N minutes. | This IP is locked out after repeated failures (see above). Fix the credentials, then wait. |
rate limit exceeded — slow down | More than 60 status calls in 60 s from this login and IP. |
unknown action: X. Valid actions: … | The action name is misspelt; the message lists them all. |
parameters is not valid base64 / did not decode to an object / is not valid XML | Check the parameters recipe below. |
missing or invalid service ID | ID is missing or isn't one of the SERVICEIDs in imeiservicelist or serverservicelist. |
service not found or disabled | The service is switched off. |
missing IMEI / invalid IMEI / invalid serial number | Send one 15-digit IMEI (or serial) per order. |
missing value / invalid value | Server and remote services: send the value in IMEI (or the service's first field), 1-64 printable characters. |
invalid quantity / Minimum quantity is N. / Maximum quantity is N. | Qnt is outside the service's range. |
<field>: <problem> | A required service field (see Requires) is missing or invalid. |
insufficient balance | Top up your wallet. Nothing was charged. |
service temporarily unavailable | The service can't be sold right now. Try later. |
at most 200 orders per request | Split the bulk call. |
missing order ID / unknown order | Send the REFERENCEID we returned when you placed it. |
temporarily unavailable, try again (ref …) | A problem on our side. Retry in a minute; quote the ref if you contact support. |
Authentication
Send your API key as a Bearer token. It is the same key a Dhru panel uses, with the same rule: calls must come from the IP the key is bound to. Create one under Account → API keys.
Authorization: Bearer imf_your_key
Base URL https://imeifixer.com/api/rest/v1. Responses are JSON. Money values are strings with four decimals: parse them as decimals, not floats.
Routes
| Route | What it does |
|---|---|
| GET /account | Your balance and login email. |
| GET /services | The Dhru imeiservicelist and serverservicelist, priced for you, under imei and server, plus remote services under remote, in the same shape. A kind this store doesn't sell is {}. Place any kind with POST /orders. |
| POST /orders | Place one order. The JSON body uses the same keys as the Dhru placeimeiorder parameters: ID, IMEI, Qnt and any service fields. |
| GET /orders/{reference_id} | One order's STATUS and CODE (the result). |
GET /account
Your balance and login email.
curl https://imeifixer.com/api/rest/v1/account \ -H 'Authorization: Bearer imf_your_key'
{
"credit": "20.0000",
"mail": "you@example.com",
"currency": "USD"
}GET /services
The Dhru imeiservicelist and serverservicelist, priced for you, under imei and server, plus remote services under remote, in the same shape. A kind this store doesn't sell is {}. Place any kind with POST /orders.
curl https://imeifixer.com/api/rest/v1/services \ -H 'Authorization: Bearer imf_your_key'
{
"imei": {
"Apple": {
"GROUPNAME": "Apple",
"SERVICES": [
{
"SERVICEID": "12",
"SERVICENAME": "iPhone Carrier Check",
"CREDIT": "2.0000",
"TIME": "Instant",
"QNT": 1,
"Requires": {}
}
]
}
},
"server": {
"Server tools": {
"GROUPNAME": "Server tools",
"SERVICES": [
{
"SERVICEID": "42",
"SERVICENAME": "Tool credits",
"CREDIT": "5.0000",
"TIME": "Instant",
"QNT": 1,
"Requires": {}
}
]
}
},
"remote": {}
}POST /orders
Place one order. The JSON body uses the same keys as the Dhru placeimeiorder parameters: ID, IMEI, Qnt and any service fields.
curl -X POST https://imeifixer.com/api/rest/v1/orders \
-H 'Authorization: Bearer imf_your_key' \
-H 'Content-Type: application/json' \
-d '{"ID":"12","IMEI":"354800000012950","Qnt":1}'{
"REFERENCEID": "IF1",
"MESSAGE": "Order placed."
}GET /orders/{reference_id}
One order's STATUS and CODE (the result).
curl https://imeifixer.com/api/rest/v1/orders/IF1 \ -H 'Authorization: Bearer imf_your_key'
{
"STATUS": "4",
"CODE": "Model: iPhone 13\nIMEI: 354800000012950\nFind My: OFF\nBlacklist: Clean"
}STATUS values
| STATUS | Meaning | Details |
|---|---|---|
| 0 | New | Paid and queued for the supplier. |
| 1 | In process | Accepted by the supplier; we keep checking it. |
| 3 | Rejected | Rejected, or refunded because it could not be done. The charge is back in your balance automatically. |
| 4 | Success | Done. The result is in CODE. |
These are the Dhru Fusion codes, so a stock panel maps them without changes: 4 is success and 3 is rejected. If your panel was set up with the two swapped, put them back to the Dhru default.
We check with the supplier about every 60 seconds, so there is no point polling one order faster than that.
Callbacks (push instead of polling)
Set a callback URL on an API key in API keys and we POST each order placed with that key to it as soon as the order is completed, rejected or refunded. You can keep polling as a fallback; the callback just tells you sooner.
- The URL must be
https://on the standard port, with a public host name. We don't follow redirects. - Answer with any
2xxwithin 10 seconds. Anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h and 6 h; then we give up. The key's page lists the last 20 deliveries and what your server answered. - After we give up, the delivery shows Gave up on the key's page. Nothing is lost:
getimeiorder(orGET /orders/<reference_id>on REST) still returns the result, so poll it. Once your endpoint works again, ask support to resend the callback and quote theREFERENCEID. - A delivery can arrive twice (for example after a timeout on your side). Use
reference_id(or theX-Imeifixer-Deliveryheader) to ignore repeats. statusis the Dhru STATUS value:4completed,3rejected or refunded.codeis only present for a completed order.
POST /your/callback/path HTTP/1.1 Content-Type: application/json User-Agent: imeifixer-callbacks/1 X-Imeifixer-Delivery: 8812 X-Imeifixer-Signature: t=1791109267,v1=5f1c0b0e…(64 hex characters)
{
"event": "order.finished",
"reference_id": "IF10452",
"status": "4",
"status_text": "completed",
"code": "Model: iPhone 13\nIMEI: 354800000012950\nFind My: OFF",
"completed_at": "2026-10-04T10:21:07.512918+00:00"
}| Field | Meaning |
|---|---|
| event | Always order.finished (a test from the key page sends ping). |
| reference_id | The REFERENCEID you got when you placed the order. |
| status | Dhru STATUS: 4 completed, 3 rejected/refunded. |
| status_text | completed, rejected or refunded. |
| code | The result (unlock code, check report). Completed orders only. |
| completed_at | When the order finished (ISO 8601, UTC). |
Check the signature before trusting the body. X-Imeifixer-Signature is t=<unix seconds>,v1=<hex>, where the hex is HMAC-SHA256 of <t>.<raw body> keyed with the key's signing secret. Use the raw bytes you received, not re-encoded JSON, and refuse timestamps more than 5 minutes old.
import hashlib, hmac, time
SECRET = "whsec_..." # shown once when you set the callback URL
def verify(raw_body: bytes, header: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
timestamp = int(parts.get("t", "0"))
if abs(time.time() - timestamp) > tolerance:
return False # too old: a replay
signed = f"{timestamp}.".encode() + raw_body
expected = hmac.new(SECRET.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))import crypto from "node:crypto";
const SECRET = process.env.IMEIFIXER_CALLBACK_SECRET; // "whsec_..."
export function verify(rawBody /* Buffer */, header, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const timestamp = Number(parts.t);
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = crypto
.createHmac("sha256", SECRET)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
const given = Buffer.from(parts.v1 ?? "", "utf8");
return given.length === expected.length && crypto.timingSafeEqual(given, Buffer.from(expected));
}<?php
$secret = 'whsec_...';
$raw = file_get_contents('php://input'); // the raw body, before json_decode
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_IMEIFIXER_SIGNATURE'] ?? ''), $sig);
$ok = isset($sig['t'], $sig['v1'])
&& abs(time() - (int) $sig['t']) <= 300
&& hash_equals(hash_hmac('sha256', $sig['t'] . '.' . $raw, $secret), $sig['v1']);
if (!$ok) { http_response_code(400); exit; }
$event = json_decode($raw, true);
http_response_code(200);Errors
Every error body carries the text under error: {"error": "<text>"}. A missing service field also names it: {"error": "missing field", "field": "<key>", "hint": …}.
| HTTP | error | When |
|---|---|---|
| 400 | invalid JSON body, body must be a JSON object, or a placement error | Placement errors use the same texts as the Dhru gateway (for example insufficient balance, invalid IMEI). |
| 401 | invalid credentials | Missing or wrong key, a revoked key, or a call from an IP the key isn't bound to. |
| 404 | unknown order | No order with that reference on your account. |
| 429 | rate limit exceeded, or the lockout message | Over 120 requests a minute on this key, or this IP is locked after 10 failed checks in 15 minutes. Wait the number of seconds in the Retry-After header. |
| 503 | temporarily unavailable | A problem on our side. Retry shortly. The body also carries a request_id: quote it if you contact support. |
Every rate limit and cap in one place: the Limits page.
Services with extra fields (CUSTOMFIELD)
Some services need more than the IMEI: a carrier, a model, a username. Each service-list row names them in Requires and in Requires.Custom, one entry per field with fieldname (the field key), fieldtype, label, required and, for a choice, fieldoptions. Every product page shows the same keys under Order by API.
Dhru: put CUSTOMFIELD inside parameters: the base64 of a JSON object keyed by field key (plain JSON is accepted too), at most 8 KB, each value text or a number. A key matches the field key in any case, or the field's label. A key sent at the top level of parameters wins over the same key in CUSTOMFIELD.
{
"carrier": "AT&T",
"model": "iPhone 13"
}{
"ID": "12",
"IMEI": "354800000012950",
"Qnt": 1,
"CUSTOMFIELD": "eyJjYXJyaWVyIjoiQVQmVCIsIm1vZGVsIjoiaVBob25lIDEzIn0="
}REST: send the same keys in the POST /orders body, next to ID, IMEI and Qnt.
{
"ID": "12",
"IMEI": "354800000012950",
"Qnt": 1,
"carrier": "AT&T",
"model": "iPhone 13"
}A required field that is missing is refused with its key and an example: Dhru missing field "carrier": send it in CUSTOMFIELD, e.g. {"carrier": "…"} (base64 JSON), REST 400 {"error": "missing field", "field": "carrier", "hint": …}. Nothing is charged.
For a server or remote service, if you leave IMEI out, its first field (other than a photo) is used as the order's value.
Photo fields (fieldtype image) take a one-time upload token, and uploading a photo needs your signed-in web account, not an API key. Order services with a photo field on the website.
Service types (SERVICETYPE)
Every service-list row carries SERVICETYPE: IMEI, SERVER or REMOTE. imeiservicelist lists IMEI services only. serverservicelist lists server services and remote ones, tagged REMOTE, since Dhru has no list action for remote services. When a server and a remote category share a name, the remote group's name ends in (Remote). REST GET /services keeps the three apart under imei, server and remote.
| SERVICETYPE | Listed by | IMEI carries |
|---|---|---|
| IMEI | imeiservicelist; REST GET /services → imei | The device's 15-digit IMEI, or its serial for serial-number services |
| SERVER | serverservicelist; REST GET /services → server | The value the service needs: a username, an email, a licence id… |
Any type is placed with placeimeiorder or placeserverorder (REST POST /orders) and polled with getimeiorder or getserverorder. Types this store doesn't sell aren't listed.
First-use binding
When you create a key you choose what it is bound to: This IP, or The first server that calls, for when you don't know your panel's IP yet.
- A first-use key has no IP yet. The first call that passes every check (right username and key, an active reseller account, an IP that isn't locked out) binds the key to that call's IP. From then on it works only from that IP, like any other key.
- That first call must come within 24 hours of creating the key. After that the key is refused and revoked automatically: create a new one.
- Make the first call from your panel's server, not a laptop. The panel's connection test, which calls
accountinfo, is enough. - If two servers make the first call at the same moment, exactly one binds the key. The other gets the invalid credentials error, and the call shows as refused in the key's Recent calls.
- When the key binds, we email you and add a notification with the IP ("Key ••••1234 is now bound to 203.0.113.10"). Not you? Revoke the key.
- To move servers, use Reset binding on the key under Account → API keys: it waits for a first call again, for another 24 hours. Reset works only on first-use keys; for a key made for one IP, create a first-use key instead.
What's new for resellers
Additions only: nothing your panel already sends or reads has changed.
October 2026
- Services with extra fields: send them in CUSTOMFIELD (Dhru) or in the POST /orders body (REST); each service-list row names them in Requires.Custom.
- Every service-list row carries SERVICETYPE (IMEI, SERVER or REMOTE); serverservicelist also lists remote services, tagged REMOTE.
- API keys can bind to the first server that calls them, within 24 hours of creating the key.
- Services with a photo field are ordered on the website: uploading a photo needs your signed-in account.
- Every product page shows its Order by API example with the exact field keys.
Migrating from the old API
If you already used imeifixer's API, nothing needs to change on your side:
| What | After the upgrade |
|---|---|
| URLs | Unchanged: /api/index.php, /api/v1/services and /api/v1/services/api/index.php for Dhru panels, /api/rest/v1 for REST. |
| Actions and field names | Unchanged (username, apiaccesskey, action, parameters, ID, IMEI, Qnt …). |
| REFERENCEIDs | Orders you placed before keep their REFERENCEID and still answer getimeiorder. |
| Service IDs | Unchanged, so your panel's product mapping keeps working. |
| API keys | Your keys carry over and stay bound to the same IP. Only replace one if we ask you to. |
| STATUS | 4 = success, 3 = rejected, as in every Dhru Fusion panel. |
Need help?
Open a support ticket with the REFERENCEID, or the time of the call from Recent calls on your API keys page. Never send us your full API key. For a callback that gave up, quote its REFERENCEID and we can send it again.
Calls failing for everyone at once? Check the status page first.