Domain Reseller: WHMCS module and API

Your Domain Reseller Plan lets you register and manage .tz domains from your own WHMCS (or from your own software) at wholesale prices. Registrations and renewals are charged to your reseller wallet; transfers are free. This page covers everything you need: where to find your credentials, how to install the WHMCS module, and the full API reference.

1. Where to find what you need

2. Install the WHMCS module

  1. Download the zip and unzip it. Upload the modules folder into the root of your WHMCS (it adds modules/registrars/sakurareseller/).
  2. In your WHMCS admin go to Configuration → System Settings → Domain Registrars, find Sakura .TZ Domain Reseller and click Activate.
  3. Enter your API Key and API Secret. Leave the endpoint as it is. Save.
  4. Go to Configuration → System Settings → Domain Pricing. Add the extensions you sell (.co.tz, .or.tz, .ac.tz, .go.tz, .tz), set Auto Registration to Sakura .TZ Domain Reseller and set your own selling prices. Your wholesale prices are on your service page.
  5. Place a test order for a domain you want. WHMCS registers it through the module and your wallet is charged automatically.

The module supports registration, renewal, transfer in, nameservers, registrar lock, EPP/auth code, contact details, expiry sync and availability search. Deleting a domain is not offered; contact support if you need one removed.

3. API reference

Use the API directly if you are not using WHMCS.

URL: https://billing.sakurahost.co.tz/modules/addons/sakura_reseller/api.php?endpoint=ENDPOINT
Method: POST with a JSON body (send {} when there are no fields).

Authentication

Every request carries three headers:

HeaderValue
AuthorizationBearer YOUR_API_KEY
X-TimestampCurrent Unix time in seconds (must be within 5 minutes of our clock)
X-SignatureLower-case hex of HMAC-SHA256(API_SECRET, TIMESTAMP + ":" + RAW_BODY)

Sign the exact bytes you send as the body. Keep the API Secret on your server only; never put it in browser code or a mobile app.

# shell
BODY='{"domains":["example.co.tz"]}'
TS=$(date +%s)
SIG=$(printf '%s' "$TS:$BODY" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | awk '{print $NF}')
curl -X POST "https://billing.sakurahost.co.tz/modules/addons/sakura_reseller/api.php?endpoint=domain/check" \
  -H "Authorization: Bearer $API_KEY" -H "X-Timestamp: $TS" -H "X-Signature: $SIG" \
  -H "Content-Type: application/json" -d "$BODY"
// PHP
$body = json_encode(['domains' => ['example.co.tz']]);
$ts   = time();
$sig  = hash_hmac('sha256', $ts . ':' . $body, $apiSecret);
$ch = curl_init('https://billing.sakurahost.co.tz/modules/addons/sakura_reseller/api.php?endpoint=domain/check');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Content-Type: application/json', "Authorization: Bearer $apiKey", "X-Timestamp: $ts", "X-Signature: $sig"]]);
$result = json_decode(curl_exec($ch), true);

Responses

Success: {"status":"success","data":{...}}
Error: {"status":"error","message":"..."}

HTTPMeaning
200Done
400 / 422Request or field problem; the message says which field
401Authentication failed (key, signature or timestamp)
402Wallet balance too low for this action
403Not allowed: suspended account, domain limit reached, address not on your allow-list, or the domain is not in your account
404 / 409Unknown endpoint / the domain already exists
429Too many requests, or too many failed logins (wait 10 minutes)
502 / 503The .tz registry returned an error or is unavailable. You are not charged when this happens

Endpoints

EndpointBody fieldsReturns
account/balancenonebalance, currency, tier, domain_limit, domains_used
pricing/tldsnoneyour register / renew / transfer price per extension
domain/checkdomains: array of up to 20 namesresults: name → available / registered / unknown
domain/suggestquery, optional limitsuggested names to check
domain/registerdomain, years (1-10), nameservers (2 to 10), registrant objectregistered, expiry, charged
domain/renewdomain, yearsrenewed, new expiry, charged
domain/transferdomain, auth_codetransfer_initiated, charged (always 0). Transfers are free and keep the domain's current expiry date
domain/infodomainstatus, locked, expiry, created, transfer_pending
domain/listnoneevery domain in your account with status and expiry
domain/nameserversdomain; add nameservers (array) to change themcurrent nameservers, or updated
domain/lockdomain, locked (true / false)locked
domain/contactsdomain; add registrant to change itregistrant details, or updated
domain/eppcodedomaina new transfer (auth) code

The registrant object

{
  "firstname": "Amina", "lastname": "Juma", "companyname": "Optional Ltd",
  "email": "amina@example.com", "phonenumber": "+255.712345678",
  "address1": "Samora Avenue 12", "address2": "", "city": "Dar es Salaam",
  "state": "Dar es Salaam", "postcode": "11101", "country": "TZ"
}

Required: firstname, lastname, email, phonenumber, address1, city. Phone numbers are written like +255.712345678; a Tanzanian number such as 0712345678 is also accepted.

Example: register a domain

POST ...api.php?endpoint=domain/register
{"domain":"example.co.tz","years":1,
 "nameservers":["ns1.yourdns.co.tz","ns2.yourdns.co.tz"],
 "registrant":{ ...as above... }}

200 {"status":"success","data":{"domain":"example.co.tz","registered":true,
     "expiry":"2027-10-26","charged":25000,"currency":"TZS"}}

4. How charging works

5. Limits and security

6. Support

Email hello@sakuragroup.co.tz or WhatsApp +255 758 309 999. Please include the domain name and the exact message you received.