AnkSOFT Reseller API v1

Reseller API Documentation
Order, Provision and Manage in One Interface.

Order and manage AnkSOFT services from your own system. A plain REST/JSON interface, prepaid balance, sandbox, webhooks and ready WHMCS and WiseCP modules.

Base URL https://www.anksoft.net/api/bayi/v1

Overview

Getting started

The AnkSOFT Reseller API is a REST/JSON interface for ordering and managing AnkSOFT services from your own system (WHMCS, WiseCP or your own software). Orders are paid from your balance and provisioned automatically.

TopicValue
Base URLhttps://www.anksoft.net/api/bayi/v1
FormatJSON (UTF-8). Only GET and POST are used.
MoneyStrings with 2 decimals, for example "206.91". Currency TRY.
VATCatalog and price lists are VAT excluded; quotes and order totals include VAT (subtotal, tax, total).
Request IDEvery response carries an X-Request-Id header. Quote it when contacting support.

1. Open the Reseller Panel

Any AnkSOFT client enables reselling on their own: Client Area > Affiliates > Reseller Panel. You apply by uploading your tax certificate, signature circular and identity document; sandbox keys work during the review and live keys open after approval. The Reseller Panel creates your API keys, the IP allowlist of each key and your webhook URL.

2. Key types

PrefixEnvironmentDescription
ank_live_LivePlaces real orders charged to your balance.
ank_test_SandboxSame endpoints; nothing real is created. Uses a separate fake balance of 100,000.00 TRY.

Key format: ank_live_<12 hex>_<43 base64url> (sandbox: ank_test_...).

3. IP allowlist

Keys with orders.write or domains.write only work from the addresses in that key's IP allowlist. A request from another address gets 403 ip_not_allowed; such a key without an allowlist gets 403 ip_allowlist_required. Add the public IP address of your WHMCS or WiseCP server.

4. Scopes

Give every key only the scopes it needs. Without the required scope the API returns 403 insufficient_scope.

ScopeAllowsEndpoints
catalog.readRead the product catalog and pricesGET /catalog, GET /catalog/{id}
orders.writeGet quotes and place orders (IP allowlist required)POST /orders/quote, POST /orders
services.readRead services, jobs and VDS statusGET /services, GET /services/{id}, GET /jobs/{id}, POST /services/{id}/actions/status
services.powerStart, stop and restart a VDSPOST /services/{id}/actions/start, .../stop, .../restart
services.cancelCancel a servicePOST /services/{id}/cancel
services.credentialsRead login detailsGET /services/{id}/credentials
billing.readRead the balanceGET /balance
domains.readRead domain prices, availability and domainsGET /domains/tlds, GET /domains/check, GET /domains, GET /domains/{id}, GET /domains/{id}/epp
domains.writeRegister, transfer and change domains (IP allowlist required)POST /domains, POST /domains/{id}/nameservers, .../lock, .../auto-renew

5. Your first order

  1. Read the catalog: GET /catalog
  2. Get the exact amount: POST /orders/quote
  3. Place the order: POST /orders (with an Idempotency-Key header)
  4. Follow the setup job: GET /jobs/{id} or the service.created webhook
  5. Fetch login details: GET /services/{id}/credentials
End to end with the PHP library
<?php
require __DIR__ . '/AnksoftBayiClient.php';

$api = new AnksoftBayiClient(getenv('ANKSOFT_API_KEY'));

$products = $api->catalog();                                   // 1. catalog
$quote    = $api->quote(105, 'monthly', ['90' => 301]);          // 2. quote (VAT included)
$order    = $api->createOrder(105, 'monthly', 'cust-42',         // 3. order
                              'vm1.example.com', 'order-1001', ['90' => 301]);
$job      = $api->waitForJob($order['job_id']);                  // 4. setup job

if ($job['status'] === 'done') {
    $login = $api->credentials($order['service_id']);  // 5. login details
}
Bearer key

Authentication

Send your key in the Authorization header of every request. A missing or invalid key returns 401 unauthorized.

Header
Authorization: Bearer ank_live_<12 hex>_<43 base64url>
Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/balance" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
Response 401
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key."
  }
}

Account conditions

  • Reseller API not enabled on your account: 403 reseller_not_active.
  • The API works in TRY; an unsupported account currency returns 403 unsupported_currency.
  • Missing scope: 403 insufficient_scope (the scope field names it).

Keep the key on the server side only; never put it in a browser, a mobile app or a public repository.

Use a separate key with the fewest scopes for every system. If you think a key leaked, create a new one in the Reseller Panel and remove the old one.

Quotas

Limits and rate limits

Request rate

TypePer key per minute
Read120
Write30
Order10

Over the limit the API returns 429 rate_limited with a Retry-After header (seconds). Wait that long and retry.

Spending and service limits

LimitValueError
Key daily spending limitEvery key has a daily spending limit in TRY.429 key_spend_limit
Account, first 30 days10 services and 5,000 TRY per day429 daily_service_limit, 429 daily_spend_limit
Account, older than 30 days50 services and 50,000 TRY per day429 daily_service_limit, 429 daily_spend_limit

AnkSOFT may change the account limits.

Other

  • The request body can be at most 64 KB; larger bodies get 413 payload_too_large.
  • When the system is busy you get 503 busy with Retry-After.
Error handling

Error codes

Errors come with an HTTP status and the body below. Some codes add extra fields (required, scope, field, option_id). Make decisions in your code on code, not on message.

Error body
{
  "error": {
    "code": "insufficient_balance",
    "message": "Insufficient balance.",
    "required": "255.49"
  }
}
HTTPCodeMeaning
400invalid_jsonThe body is not valid JSON.
400invalid_idempotency_keyIdempotency-Key missing or invalid (8 to 64 characters, A-Z a-z 0-9 _ -).
401unauthorizedMissing or invalid key.
402insufficient_balanceInsufficient balance. The required field holds the amount needed.
403reseller_not_activeThe reseller API is not active on your account.
403insufficient_scopeThe key lacks the required scope. The scope field names it.
403ip_not_allowedThe request came from an address outside the key's IP allowlist.
403ip_allowlist_requiredThis key needs an IP allowlist (orders.write, domains.write).
403unsupported_currencyUnsupported account currency; the API works in TRY.
404not_foundResource not found. Also returned for resources of other accounts.
404product_not_foundProduct not found or not available to you.
405method_not_allowedThe endpoint does not accept this HTTP method.
409out_of_stockThe product is out of stock.
409idempotency_conflictThe same Idempotency-Key was sent with a different body.
409request_in_progressThe first request with this key is still being processed; repeat the same request a little later.
409price_changedThe price changed; fetch the catalog or a quote again.
409service_not_activeThe action needs an active service.
409service_not_readyThe service is not ready yet; retry after the setup job finishes.
409action_not_supportedPower actions are not supported for this service type; status, start, stop and restart are VDS only.
409domain_not_availableThe domain is not available.
409domain_not_activeThe action needs an active domain.
413payload_too_largeThe body is larger than 64 KB.
422invalid_parameterA field is invalid. The field field names it.
422invalid_end_customer_refend_customer_ref must be 1 to 64 characters: A-Z a-z 0-9 . _ -.
422not_sellableThe product or billing cycle is not sellable to you.
422invalid_scopeInvalid scope name.
422invalid_ipInvalid IP address.
422invalid_optionInvalid product option or value. The option_id field names the option.
422tld_not_availableThe TLD or action is not sellable to you at the moment.
429rate_limitedPer minute rate limit exceeded; wait for Retry-After.
429daily_service_limitThe account's daily service limit is reached.
429daily_spend_limitThe account's daily spending limit is reached.
429key_spend_limitThe key's daily spending limit is reached.
502order_failedThe order failed and nothing was created. It is safe to retry with a new Idempotency-Key.
502order_state_unknownThe order state is unknown. Do not retry; contact support with the X-Request-Id.
503service_closedThe reseller API is temporarily closed.
503service_unavailableThe service is temporarily unavailable.
503busyThe system is busy; wait for Retry-After.

When to retry

SituationWhat to do
Network error or timeout (order)Resend with the same Idempotency-Key and the same body.
429, 503 busyWait for Retry-After and retry.
502 order_failedRetry with a new Idempotency-Key.
502 order_state_unknownDo not retry; contact support.
Job status failedNever place the order again; our support team completes it.
4xxDo not resend without fixing the request.
Error handling with the PHP library
try {
    $order = $api->createOrder(105, 'monthly', 'cust-42', 'vm1.example.com', 'order-1001');
} catch (AnksoftBayiException $e) {
    // $e->httpStatus, $e->errorCode, $e->details, $e->retryAfter
    if ($e->errorCode === 'insufficient_balance') {
        $needed = $e->details['required'];
    } elseif ($e->httpStatus === 429) {
        sleep($e->retryAfter ?? 60);
    }
}
Products

Catalog and product options

The catalog lists the products you can sell and the price of every billing cycle you can buy. Prices already include your tier discount and exclude VAT.

Product object

FieldTypeDescription
idintegerProduct ID; product_id when ordering.
namestringProduct name.
groupstringProduct group.
typestringvds, hosting or game.
in_stockbooleanStock state. Ordering an out of stock product returns 409 out_of_stock.
hostname_requiredbooleanWhen true (hosting) send the customer's domain as hostname.
currencystringTRY
tax_excludedbooleanPrices exclude VAT (true).
cyclesobjectCycle name → {price, setup}. Only cycles you can buy are listed.
optionsarrayOption objects.

Option object

FieldTypeDescription
idintegerOption ID; the key in options when ordering.
namestringOption name.
typestringselect, quantity or yesno.
defaultintegerValue used when you do not send the option: choice ID (select), quantity (quantity) or 0/1 (yesno).
min, maxintegerQuantity only: allowed range.
choicesarraySelect only: {id, name, cycles} choices.
cyclesobjectQuantity: price per unit; yesno: price when 1.

Billing cycles: monthly, quarterly, semiannually, annually, biennially, triennially.

GET/catalog
catalog.read

All products you can sell.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/catalog" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$products = $api->catalog();   // [Product, ...]
Response 200
{
  "data": [
    {
      "id": 105,
      "name": "VDS 1",
      "group": "Xeon Vds Sunucu",
      "type": "vds",
      "in_stock": true,
      "hostname_required": false,
      "currency": "TRY",
      "tax_excluded": true,
      "cycles": {
        "monthly": {
          "price": "206.91",
          "setup": "0.00"
        },
        "quarterly": {
          "price": "620.73",
          "setup": "0.00"
        }
      },
      "options": [
        {
          "id": 90,
          "name": "İşletim Sistemi Seçiniz",
          "type": "select",
          "default": 301,
          "choices": [
            {
              "id": 301,
              "name": "Ubuntu 24.04",
              "cycles": {
                "monthly": {
                  "price": "0.00",
                  "setup": "0.00"
                }
              }
            }
          ]
        },
        {
          "id": 88,
          "name": "Ek Disk (GB)",
          "type": "quantity",
          "default": 0,
          "min": 0,
          "max": 50,
          "cycles": {
            "monthly": {
              "price": "3.00",
              "setup": "0.00"
            }
          }
        }
      ]
    }
  ]
}
GET/catalog/{id}
catalog.read

A single product. 404 product_not_found when it does not exist or is not available to you.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/catalog/105" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$product = $api->product(105);
Response 200
{
  "id": 105,
  "name": "VDS 1",
  "group": "Xeon Vds Sunucu",
  "type": "vds",
  "in_stock": true,
  "hostname_required": false,
  "currency": "TRY",
  "tax_excluded": true,
  "cycles": {
    "monthly": {
      "price": "206.91",
      "setup": "0.00"
    },
    "quarterly": {
      "price": "620.73",
      "setup": "0.00"
    }
  },
  "options": [
    {
      "id": 90,
      "name": "İşletim Sistemi Seçiniz",
      "type": "select",
      "default": 301,
      "choices": [
        {
          "id": 301,
          "name": "Ubuntu 24.04",
          "cycles": {
            "monthly": {
              "price": "0.00",
              "setup": "0.00"
            }
          }
        }
      ]
    },
    {
      "id": 88,
      "name": "Ek Disk (GB)",
      "type": "quantity",
      "default": 0,
      "min": 0,
      "max": 50,
      "cycles": {
        "monthly": {
          "price": "3.00",
          "setup": "0.00"
        }
      }
    }
  ]
}
Before ordering

Price quote

A quote returns the exact amount, VAT included, that an order would charge. It creates nothing.

POST/orders/quote
orders.writeIP allowlist

Amount for a product, billing cycle and options.

Request body

FieldTypeRequiredDescription
product_idintegerYesProduct ID from the catalog.
billing_cyclestringYesmonthly, quarterly, semiannually, annually, biennially, triennially
optionsobjectNoOption ID → value: choice ID (select), quantity (quantity) or 0/1 (yesno). Options you do not send use default.
Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/orders/quote" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"product_id": 105, "billing_cycle": "monthly", "options": {"90": 301, "88": 2}}'
PHP library
$quote = $api->quote(105, 'monthly', ['90' => 301, '88' => 2]);
echo $quote['total'];
Response 200
{
  "subtotal": "212.91",
  "tax": "42.58",
  "total": "255.49",
  "currency": "TRY",
  "billing_cycle": "monthly"
}
Product orders

Orders

The order is paid from your balance and the service is provisioned automatically. The response returns 202 right away; follow the setup with GET /jobs/{job_id}.

POST/orders
orders.writeIP allowlistIdempotency-Key

Place a new service order.

Request body

FieldTypeRequiredDescription
product_idintegerYesProduct ID from the catalog.
billing_cyclestringYesmonthly, quarterly, semiannually, annually, biennially, triennially
end_customer_refstringYesYour own customer reference: 1 to 64 characters, A-Z a-z 0-9 . _ -. No personal data.
hostnamestringHosting: yesOptional for vds and game; required for hosting (the customer's domain).
optionsobjectNoOption ID → value: choice ID (select), quantity (quantity) or 0/1 (yesno). Options you do not send use default.
Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/orders" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: whmcs-1001" \
  -d '{"product_id": 105, "billing_cycle": "monthly", "end_customer_ref": "cust-42", "hostname": "vm1.example.com", "options": {"90": 301, "88": 2}}'
PHP library
$order = $api->createOrder(
    105,                  // product_id
    'monthly',            // billing_cycle
    'cust-42',            // end_customer_ref
    'vm1.example.com',    // hostname
    'whmcs-1001',         // Idempotency-Key: stable, one per order
    ['90' => 301, '88' => 2]
);
// $order['service_id'], $order['job_id']
Response 202
{
  "order_id": 9001,
  "service_id": 20123,
  "invoice_id": 145000,
  "job_id": 5521,
  "status": "processing",
  "total": "255.49"
}

Idempotency: the same order is never placed twice

POST /orders and POST /domains require an Idempotency-Key header: 8 to 64 characters, A-Z a-z 0-9 _ -. The key is the identity of one order. When the connection drops or no response arrives, you can safely resend the same request with the same key; the API does not place a second order and returns the first result.

SituationResult
Same key, same bodyThe first result is returned; no second order.
Same key, different body409 idempotency_conflict
First request still in progress409 request_in_progress
Header missing or malformed400 invalid_idempotency_key
  • Derive the key from the record that triggers the order, for example whmcs-<service id>. A new random value on every attempt defeats the protection.
  • In the PHP library createOrder() and createDomain() generate a random key when you pass none; pass your own stable key for retries.
  • 502 order_failed: nothing was created; retry with a new key.
  • 502 order_state_unknown: do not retry, contact support.

Common order errors: 402 insufficient_balance, 409 out_of_stock, 409 price_changed, 422 invalid_option, 422 not_sellable and the 429 daily limits.

Provisioning

Jobs

Every order starts a job. When the job is done the service is ready.

StatusMeaning
queuedQueued.
runningBeing provisioned.
doneDone.
cancelledCancelled.
failedOur support team completes it. Never place the order again.
GET/jobs/{id}
services.read

Job status. Jobs of domain orders are followed here too.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/jobs/5521" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$job = $api->job(5521);

// poll until finished: at most 120 s, every 5 s
$job = $api->waitForJob(5521, 120, 5);
Response 200
{
  "id": 5521,
  "type": "order",
  "status": "done"
}
Status, power, cancel, credentials

Services

Service object

FieldTypeDescription
idintegerService ID (service_id).
order_id, invoice_idintegerInitial order and invoice.
end_customer_refstringThe reference you sent when ordering.
type, product_idstring, integerProduct type and ID.
statusstringpending, active, suspended, terminated, cancelled, fraud
next_due_date, billing_cyclestringNext due date (YYYY-MM-DD) and billing cycle.
capabilitiesarrayAvailable actions. VDS: status, start, stop, restart, cancel; hosting and game: cancel.
hostname, ip, ipsstring, string, arraySingle service response only.
GET/services
services.read

Your services, 100 per page. Pass the last id as after for the next page.

Query parameters

FieldTypeRequiredDescription
afterintegerNoLast service ID of the previous page.
Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/services?after=20100" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$after = 0;
do {
    $page = $api->services($after);
    foreach ($page as $s) {
        $after = $s['id'];
    }
} while (count($page) === 100);
Response 200
{
  "data": [
    {
      "id": 20123,
      "order_id": 9001,
      "invoice_id": 145000,
      "end_customer_ref": "cust-42",
      "type": "vds",
      "product_id": 105,
      "status": "active",
      "next_due_date": "2026-12-09",
      "billing_cycle": "monthly",
      "capabilities": [
        "status",
        "start",
        "stop",
        "restart",
        "cancel"
      ]
    }
  ]
}
GET/services/{id}
services.read

A single service, including hostname, ip and ips.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/services/20123" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$service = $api->service(20123);
Response 200
{
  "id": 20123,
  "order_id": 9001,
  "invoice_id": 145000,
  "end_customer_ref": "cust-42",
  "type": "vds",
  "product_id": 105,
  "status": "active",
  "next_due_date": "2026-12-09",
  "billing_cycle": "monthly",
  "capabilities": [
    "status",
    "start",
    "stop",
    "restart",
    "cancel"
  ],
  "hostname": "vm1.example.com",
  "ip": "185.118.141.120",
  "ips": []
}
GET/services/{id}/credentials
services.credentials

Access details to hand over to your customer. Active services only. Do not log or store the password in plain text.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/services/20124/credentials" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$login = $api->credentials(20124);
Response 200
{
  "service_id": 20124,
  "hostname": "ornekmusteri.com",
  "ip": "185.118.141.30",
  "ips": [],
  "username": "ornekmus",
  "password": "<password>",
  "server_hostname": "hosting1.example.net",
  "server_ip": "185.118.141.30"
}
FieldTypeDescription
service_idintegerService ID.
hostname, ip, ipsstring, string, arrayService host name and IP addresses.
username, passwordstringLogin details.
server_hostname, server_ipstringHosting: control panel server; game: the panel.
POST/services/{id}/actions/status
services.read

Live VDS state. state: running, off or unknown. VDS only; other types get 409 action_not_supported.

No request body.

Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/services/20123/actions/status" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$st = $api->status(20123);
Response 200
{
  "state": "running",
  "cpu_percent": 12,
  "ram_percent": 41
}
POST/services/{id}/actions/start
POST/services/{id}/actions/stop
POST/services/{id}/actions/restart
services.power

Start, stop and restart a VDS. VDS only; other types get 409 action_not_supported.

No request body.

Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/services/20123/actions/restart" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$api->start(20123);
$api->stop(20123);
$api->restart(20123);
Response 200
{
  "ok": true,
  "action": "restart"
}
POST/services/{id}/cancel
services.cancel

Cancels the service on our side. end_of_period cancels at the end of the current period (no renewal invoice), immediate cancels now.

Renewals are paid automatically from your balance: cancel services your customer no longer pays for.

Request body

FieldTypeRequiredDescription
typestringYesend_of_period or immediate
reasonstringNoCancellation reason.
Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/services/20123/cancel" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "end_of_period", "reason": "customer left"}'
PHP library
$api->cancel(20123);                         // end of period
$api->cancel(20123, true, 'customer left');    // immediately
Response 202
{
  "ok": true,
  "service_id": 20123,
  "type": "end_of_period"
}
Prepaid account

Balance

Orders and renewals are paid from your balance. Top it up by bank transfer in the client area. When it drops below the threshold you get a balance.low webhook.

GET/balance
billing.read

balance total balance, reserved held for orders not yet paid, available amount you can spend.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/balance" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$b = $api->balance();
echo $b['available'];
Response 200
{
  "balance": "12500.00",
  "reserved": "255.49",
  "available": "12244.51",
  "currency": "TRY"
}
Register, transfer, manage

Domains

Reads need domains.read; orders and changes need domains.write (IP allowlist required). The registrant is your account's details.

Price is the AnkSOFT domain list price minus your tier discount (same as the client area cart). A TLD or action is sellable to you while that price stays at least a safety margin above the registry cost, which AnkSOFT reads daily from the registrar. Otherwise it is omitted from /domains/tlds and ordering returns 422 tld_not_available.

The sellable TLDs can change with the cost rule; do not hard code the list, always fetch current TLDs and prices with GET /domains/tlds. Name server, lock and auto renew changes need an active domain; otherwise 409 domain_not_active.

Domain object

FieldTypeDescription
id, domain, order_idinteger, string, integerDomain record.
end_customer_refstringThe reference you sent when ordering.
statusstringpending, active, expired, transferred_away, cancelled and others.
registration_date, expiry_date, next_due_datestringYYYY-MM-DD
auto_renewbooleanWhen false it is not renewed from your balance at expiry.
nameserversarrayName servers.
GET/domains/tlds
domains.read

TLDs you can sell with register, transfer and renew prices per number of years (VAT excluded).

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/domains/tlds" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$tlds = $api->domainTlds();
Response 200
{
  "data": [
    {
      "tld": ".net",
      "register": {
        "1": "737.10",
        "2": "1474.20"
      },
      "transfer": {
        "1": "737.10"
      },
      "renew": {
        "1": "737.10"
      },
      "currency": "TRY",
      "tax_excluded": true
    }
  ]
}
GET/domains/check
domains.read

Availability and the 1 year register price.

Query parameters

FieldTypeRequiredDescription
domainstringYesDomain to check.
Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/domains/check?domain=example.net" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$r = $api->domainCheck('example.net');
Response 200
{
  "domain": "example.net",
  "tld": ".net",
  "available": true,
  "register_price_1y": "737.10"
}
POST/domains
domains.writeIP allowlistIdempotency-Key

Register or transfer a domain, paid from your balance. Idempotency-Key rules are the same as for product orders.

Request body

FieldTypeRequiredDescription
domainstringYesDomain name.
typestringYesregister or transfer
yearsintegerYes1 to 10
end_customer_refstringYesYour own customer reference; no personal data.
nameserversarrayNo2 to 5 name servers.
epp_codestringNoAuthorization (EPP) code for transfers.
id_protectionbooleanNoID protection.
additional_fieldsobjectNoExtra registry fields, name → value.
Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/domains" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: domain-3001" \
  -d '{"domain": "example.net", "type": "register", "years": 1, "end_customer_ref": "cust-42", "nameservers": ["ns1.example.net", "ns2.example.net"], "id_protection": false}'
PHP library
$r = $api->createDomain('example.net', 'register', 1, 'cust-42', [
    'nameservers'   => ['ns1.example.net', 'ns2.example.net'],
    'id_protection' => false,
], 'domain-3001');
Response 202
{
  "order_id": 9002,
  "domain_id": 3310,
  "invoice_id": 145001,
  "job_id": 5522,
  "status": "processing",
  "total": "884.52"
}
GET/domains
domains.read

Your domains; paginate with after as for services.

Query parameters

FieldTypeRequiredDescription
afterintegerNoLast domain ID of the previous page.
Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/domains" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$list = $api->domains();
Response 200
{
  "data": [
    {
      "id": 3310,
      "domain": "example.net",
      "order_id": 9002,
      "end_customer_ref": "cust-42",
      "status": "active",
      "registration_date": "2026-11-09",
      "expiry_date": "2027-11-09",
      "next_due_date": "2027-11-09",
      "auto_renew": true,
      "nameservers": [
        "ns1.example.net",
        "ns2.example.net"
      ]
    }
  ]
}
GET/domains/{id}
domains.read

A single domain.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/domains/3310" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$d = $api->domain(3310);
Response 200
{
  "id": 3310,
  "domain": "example.net",
  "order_id": 9002,
  "end_customer_ref": "cust-42",
  "status": "active",
  "registration_date": "2026-11-09",
  "expiry_date": "2027-11-09",
  "next_due_date": "2027-11-09",
  "auto_renew": true,
  "nameservers": [
    "ns1.example.net",
    "ns2.example.net"
  ]
}
POST/domains/{id}/nameservers
domains.writeIP allowlist

Changes the name servers.

Request body

FieldTypeRequiredDescription
nameserversarrayYesNew name servers.
Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/domains/3310/nameservers" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"nameservers": ["ns1.example.net", "ns2.example.net"]}'
PHP library
$api->setNameservers(3310, ['ns1.example.net', 'ns2.example.net']);
Response 200
{
  "ok": true
}
GET/domains/{id}/epp
domains.read

Transfer authorization (EPP) code.

Example request (curl)
curl "https://www.anksoft.net/api/bayi/v1/domains/3310/epp" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY"
PHP library
$code = $api->eppCode(3310)['epp_code'];
Response 200
{
  "epp_code": "<epp_code>"
}
POST/domains/{id}/lock
domains.writeIP allowlist

Turns the transfer lock on or off.

Request body

FieldTypeRequiredDescription
lockedbooleanYestrue locked.
Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/domains/3310/lock" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"locked": true}'
PHP library
$api->setLock(3310, true);
Response 200
{
  "ok": true
}
POST/domains/{id}/auto-renew
domains.writeIP allowlist

Auto renewal. When false the domain is not renewed from your balance at expiry.

Request body

FieldTypeRequiredDescription
enabledbooleanYestrue on.
Example request (curl)
curl -X POST "https://www.anksoft.net/api/bayi/v1/domains/3310/auto-renew" \
  -H "Authorization: Bearer $ANKSOFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'
PHP library
$api->setAutoRenew(3310, false);
Response 200
{
  "ok": true
}
Event notifications

Webhooks

Set one HTTPS URL in the Reseller Panel (port 443, public IP). Every event is sent to it as a POST with a JSON body.

Example body
{
  "id": 812,
  "event": "service.created",
  "created_at": "2026-11-09T10:00:00+03:00",
  "data": {
    "service_id": 20123,
    "order_id": 9001,
    "end_customer_ref": "cust-42"
  }
}

Headers

HeaderDescription
X-AnkSOFT-EventEvent name.
X-AnkSOFT-DeliveryDelivery ID (the body id). Use it to ignore duplicates.
X-AnkSOFT-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>

Events

EventWhendata fields
service.created, service.suspended, service.unsuspended, service.terminatedService created, suspended, unsuspended, terminated.service_id, order_id, end_customer_ref, reason (suspended only)
service.renewedService renewed.service_id, end_customer_ref, invoice_id, next_due_date
invoice.createdInvoice created.invoice_id, total, due_date, services: [{service_id, end_customer_ref}]
invoice.paidInvoice paid.invoice_id, total, services
domain.registered, domain.transferred, domain.renewedDomain registered, transferred, renewed.domain_id, domain, end_customer_ref, expiry_date
balance.lowBalance below the threshold (at most once a day).balance, threshold, currency
pingTest button in the Reseller Panel.message

Signature verification

Reject the request if the signature does not match or t is older than 5 minutes. Compute it over the raw body; do not decode and re-encode the JSON.

PHP
<?php
$secret = getenv('ANKSOFT_WEBHOOK_SECRET');
$body   = file_get_contents('php://input');

parse_str(str_replace(',', '&', $_SERVER['HTTP_X_ANKSOFT_SIGNATURE'] ?? ''), $s);
$ok = isset($s['t'], $s['v1']) && abs(time() - (int) $s['t']) < 300
   && hash_equals(hash_hmac('sha256', $s['t'] . '.' . $body, $secret), $s['v1']);

if (!$ok) {
    http_response_code(400);   // bad signature or older than 5 minutes
    exit;
}

$event    = json_decode($body, true);
$delivery = $_SERVER['HTTP_X_ANKSOFT_DELIVERY'] ?? '';

// If this delivery was already processed, just return 200 (check your own store)
// Queue heavy work: the response must return within 6 seconds
http_response_code(200);

Retries

If your receiver does not return 2xx within 6 seconds, the delivery is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h. A delivery may arrive more than once; deduplicate with X-AnkSOFT-Delivery.

ank_test_ keys

Sandbox scenarios

With ank_test_ keys you use the same endpoints; nothing real is created and a separate fake balance of 100,000.00 TRY is used. Orders complete in seconds. Test your error paths with these scenarios:

ScenarioHowResult
Successful orderAny orderThe job becomes done within seconds.
Setup failurehostname: sandbox-setup-fail.testThe job ends failed.
Suspended servicehostname: sandbox-suspended.testThe service is created as suspended.
Insufficient balanceHeader X-Sandbox-Scenario: insufficient_balance402 insufficient_balance
Taken domainA name with taken in the label, for example foo.sandbox-taken.testavailable: false
Insufficient balance scenario
curl -X POST "https://www.anksoft.net/api/bayi/v1/orders" \
  -H "Authorization: Bearer $ANKSOFT_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sandbox-0001" \
  -H "X-Sandbox-Scenario: insufficient_balance" \
  -d '{"product_id": 105, "billing_cycle": "monthly", "end_customer_ref": "test-1"}'
Reseller pricing

Levels

Your tier depends on the amount paid in the last 30 days. Catalog and domain prices already include your discount.

TierDiscountPaid in the last 30 days
Başlangıç (Starter)10%0 to 4,999 TRY
Bronz (Bronze)15%5,000 to 19,999 TRY
Gümüş (Silver)20%20,000 to 49,999 TRY
Altın (Gold)25%50,000 TRY and above
  • The tier is checked at the start of each month; moving up applies immediately, moving down only after 2 months below the threshold.
  • The discount applies to recurring fees and domains. Setup fees are not discounted.
  • Combined with long term cycle discounts, a product is never sold more than 30% below its monthly list price; cycles that would exceed this are hidden.
Modules and tools

Downloads

Connect without writing code using the ready modules, or build your own integration with the PHP library.

Quick setup

WHMCS and WiseCP setup

WHMCS server module

Requirements: WHMCS 8.x, PHP 7.4+ with cURL, a reseller API key.

  1. Upload modules/servers/anksoftbayi/ from the zip to your WHMCS root.
  2. Setup > Products/Services > Servers > Add New Server: Module AnkSOFT Reseller, Hostname www.anksoft.net, your API key in Password. Click Test Connection. You can try everything with an ank_test_ key first.
  3. Create a server group with this server.
  4. Create a product (Server/VPS). In Module Settings pick the module and the group, choose the product from AnkSOFT Product, set your own selling price under Pricing and choose "Automatically setup the product as soon as the first payment is received".
  5. Key scopes: catalog.read, orders.write, services.read, services.power, services.cancel, services.credentials, billing.read. Add your WHMCS server's public IP to the key's allowlist.

Suspending in WHMCS powers the server off but AnkSOFT keeps billing you; Terminate the service when your customer leaves. Renewals are paid automatically from your AnkSOFT balance.

WHMCS registrar module

  1. Upload the modules folder from the zip to your WHMCS root.
  2. Activate the AnkSOFT module under Setup > Products/Services > Domain Registrars and enter your API key.
  3. Give the key domains.read and domains.write; add services.read to follow jobs. Add your WHMCS server's IP to the allowlist.
  4. Assign the TLDs you sell to this module under Domain Pricing.

WiseCP module

Requirements: WiseCP 3.x or WiseCP 5, PHP 7.4+ with cURL, a reseller API key (with services.credentials for login details).

  1. Copy the coremio folder from the zip into the WiseCP root: <wisecp>/coremio/modules/Servers/AnksoftBayi/AnksoftBayi.php.
  2. Products/Services > Server Settings > Add New Server: module AnkSOFT Bayi, hostname www.anksoft.net, any username, API key in Access Hash (or in Password if there is no Access Hash field). Test Connection reads your balance.
  3. On the product's Module tab pick this server and the AnkSOFT product; set the setup wait (default 45 s, max 120 s) and the cancellation type (default end of period).
  4. You set the sale price and billing cycles on the WiseCP product. Enable only 1, 3, 6, 12, 24 and 36 month cycles.
FAQ

Frequently asked questions

How do I enable the Reseller API?

Apply in Client Area > Affiliates > Reseller Panel by uploading your documents (tax certificate, signature circular, identity). You can create sandbox keys on the same page during the review; live keys open after approval.

Does a test key create real servers?

No. With ank_test_ keys nothing real is created and a separate fake balance of 100,000.00 TRY is used.

My order request timed out. Can I resend it?

Yes. Send it with the same Idempotency-Key and the same body; the API does not create a second order and returns the first result.

The setup job failed. What should I do?

Do not place the order again. Our support team completes the setup.

Do prices include VAT?

Catalog and domain price lists exclude VAT. Quotes and order totals include VAT.

How are renewals paid?

Renewals are paid automatically from your balance. Cancel services your customer no longer pays for and turn off auto renewal for domains when needed.

Do I send my customer's personal data?

No. Put only your own reference in end_customer_ref, never personal data. For domains the registrant is your account's details.

Can I change a package through the API?

The API has no package change endpoint. Open a support ticket for it.

How do I top up my balance?

By bank transfer in the client area. A balance.low webhook is sent when it drops below the threshold.

What should I include when contacting support?

The X-Request-Id header value of the response and the error code.

Start integrating

Test First With a Sandbox Key.

Create an ank_test_ key in the Reseller Panel and try the whole flow without creating real servers.

TR| EN