OverviewGetting 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.
| Topic | Value |
|---|
| Base URL | https://www.anksoft.net/api/bayi/v1 |
| Format | JSON (UTF-8). Only GET and POST are used. |
| Money | Strings with 2 decimals, for example "206.91". Currency TRY. |
| VAT | Catalog and price lists are VAT excluded; quotes and order totals include VAT (subtotal, tax, total). |
| Request ID | Every 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
| Prefix | Environment | Description |
|---|
ank_live_ | Live | Places real orders charged to your balance. |
ank_test_ | Sandbox | Same 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.
| Scope | Allows | Endpoints |
|---|
catalog.read | Read the product catalog and prices | GET /catalog, GET /catalog/{id} |
orders.write | Get quotes and place orders (IP allowlist required) | POST /orders/quote, POST /orders |
services.read | Read services, jobs and VDS status | GET /services, GET /services/{id}, GET /jobs/{id}, POST /services/{id}/actions/status |
services.power | Start, stop and restart a VDS | POST /services/{id}/actions/start, .../stop, .../restart |
services.cancel | Cancel a service | POST /services/{id}/cancel |
services.credentials | Read login details | GET /services/{id}/credentials |
billing.read | Read the balance | GET /balance |
domains.read | Read domain prices, availability and domains | GET /domains/tlds, GET /domains/check, GET /domains, GET /domains/{id}, GET /domains/{id}/epp |
domains.write | Register, transfer and change domains (IP allowlist required) | POST /domains, POST /domains/{id}/nameservers, .../lock, .../auto-renew |
5. Your first order
- Read the catalog:
GET /catalog - Get the exact amount:
POST /orders/quote - Place the order:
POST /orders (with an Idempotency-Key header) - Follow the setup job:
GET /jobs/{id} or the service.created webhook - 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 keyAuthentication
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.
QuotasLimits and rate limits
Request rate
| Type | Per key per minute |
|---|
| Read | 120 |
| Write | 30 |
| Order | 10 |
Over the limit the API returns 429 rate_limited with a Retry-After header (seconds). Wait that long and retry.
Spending and service limits
| Limit | Value | Error |
|---|
| Key daily spending limit | Every key has a daily spending limit in TRY. | 429 key_spend_limit |
| Account, first 30 days | 10 services and 5,000 TRY per day | 429 daily_service_limit, 429 daily_spend_limit |
| Account, older than 30 days | 50 services and 50,000 TRY per day | 429 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 handlingError 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"
}
}
| HTTP | Code | Meaning |
|---|
| 400 | invalid_json | The body is not valid JSON. |
| 400 | invalid_idempotency_key | Idempotency-Key missing or invalid (8 to 64 characters, A-Z a-z 0-9 _ -). |
| 401 | unauthorized | Missing or invalid key. |
| 402 | insufficient_balance | Insufficient balance. The required field holds the amount needed. |
| 403 | reseller_not_active | The reseller API is not active on your account. |
| 403 | insufficient_scope | The key lacks the required scope. The scope field names it. |
| 403 | ip_not_allowed | The request came from an address outside the key's IP allowlist. |
| 403 | ip_allowlist_required | This key needs an IP allowlist (orders.write, domains.write). |
| 403 | unsupported_currency | Unsupported account currency; the API works in TRY. |
| 404 | not_found | Resource not found. Also returned for resources of other accounts. |
| 404 | product_not_found | Product not found or not available to you. |
| 405 | method_not_allowed | The endpoint does not accept this HTTP method. |
| 409 | out_of_stock | The product is out of stock. |
| 409 | idempotency_conflict | The same Idempotency-Key was sent with a different body. |
| 409 | request_in_progress | The first request with this key is still being processed; repeat the same request a little later. |
| 409 | price_changed | The price changed; fetch the catalog or a quote again. |
| 409 | service_not_active | The action needs an active service. |
| 409 | service_not_ready | The service is not ready yet; retry after the setup job finishes. |
| 409 | action_not_supported | Power actions are not supported for this service type; status, start, stop and restart are VDS only. |
| 409 | domain_not_available | The domain is not available. |
| 409 | domain_not_active | The action needs an active domain. |
| 413 | payload_too_large | The body is larger than 64 KB. |
| 422 | invalid_parameter | A field is invalid. The field field names it. |
| 422 | invalid_end_customer_ref | end_customer_ref must be 1 to 64 characters: A-Z a-z 0-9 . _ -. |
| 422 | not_sellable | The product or billing cycle is not sellable to you. |
| 422 | invalid_scope | Invalid scope name. |
| 422 | invalid_ip | Invalid IP address. |
| 422 | invalid_option | Invalid product option or value. The option_id field names the option. |
| 422 | tld_not_available | The TLD or action is not sellable to you at the moment. |
| 429 | rate_limited | Per minute rate limit exceeded; wait for Retry-After. |
| 429 | daily_service_limit | The account's daily service limit is reached. |
| 429 | daily_spend_limit | The account's daily spending limit is reached. |
| 429 | key_spend_limit | The key's daily spending limit is reached. |
| 502 | order_failed | The order failed and nothing was created. It is safe to retry with a new Idempotency-Key. |
| 502 | order_state_unknown | The order state is unknown. Do not retry; contact support with the X-Request-Id. |
| 503 | service_closed | The reseller API is temporarily closed. |
| 503 | service_unavailable | The service is temporarily unavailable. |
| 503 | busy | The system is busy; wait for Retry-After. |
When to retry
| Situation | What to do |
|---|
| Network error or timeout (order) | Resend with the same Idempotency-Key and the same body. |
429, 503 busy | Wait for Retry-After and retry. |
502 order_failed | Retry with a new Idempotency-Key. |
502 order_state_unknown | Do not retry; contact support. |
Job status failed | Never place the order again; our support team completes it. |
4xx | Do 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);
}
}
ProductsCatalog 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
| Field | Type | Description |
|---|
id | integer | Product ID; product_id when ordering. |
name | string | Product name. |
group | string | Product group. |
type | string | vds, hosting or game. |
in_stock | boolean | Stock state. Ordering an out of stock product returns 409 out_of_stock. |
hostname_required | boolean | When true (hosting) send the customer's domain as hostname. |
currency | string | TRY |
tax_excluded | boolean | Prices exclude VAT (true). |
cycles | object | Cycle name → {price, setup}. Only cycles you can buy are listed. |
options | array | Option objects. |
Option object
| Field | Type | Description |
|---|
id | integer | Option ID; the key in options when ordering. |
name | string | Option name. |
type | string | select, quantity or yesno. |
default | integer | Value used when you do not send the option: choice ID (select), quantity (quantity) or 0/1 (yesno). |
min, max | integer | Quantity only: allowed range. |
choices | array | Select only: {id, name, cycles} choices. |
cycles | object | Quantity: price per unit; yesno: price when 1. |
Billing cycles: monthly, quarterly, semiannually, annually, biennially, triennially.
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"
}
}
}
]
}
]
}
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 orderingPrice quote
A quote returns the exact amount, VAT included, that an order would charge. It creates nothing.
Amount for a product, billing cycle and options.
Request body
| Field | Type | Required | Description |
|---|
product_id | integer | Yes | Product ID from the catalog. |
billing_cycle | string | Yes | monthly, quarterly, semiannually, annually, biennially, triennially |
options | object | No | Option 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 ordersOrders
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}.
orders.writeIP allowlistIdempotency-Key
Place a new service order.
Request body
| Field | Type | Required | Description |
|---|
product_id | integer | Yes | Product ID from the catalog. |
billing_cycle | string | Yes | monthly, quarterly, semiannually, annually, biennially, triennially |
end_customer_ref | string | Yes | Your own customer reference: 1 to 64 characters, A-Z a-z 0-9 . _ -. No personal data. |
hostname | string | Hosting: yes | Optional for vds and game; required for hosting (the customer's domain). |
options | object | No | Option 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.
| Situation | Result |
|---|
| Same key, same body | The first result is returned; no second order. |
| Same key, different body | 409 idempotency_conflict |
| First request still in progress | 409 request_in_progress |
| Header missing or malformed | 400 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.
ProvisioningJobs
Every order starts a job. When the job is done the service is ready.
| Status | Meaning |
|---|
queued | Queued. |
running | Being provisioned. |
done | Done. |
cancelled | Cancelled. |
failed | Our support team completes it. Never place the order again. |
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, credentialsServices
Service object
| Field | Type | Description |
|---|
id | integer | Service ID (service_id). |
order_id, invoice_id | integer | Initial order and invoice. |
end_customer_ref | string | The reference you sent when ordering. |
type, product_id | string, integer | Product type and ID. |
status | string | pending, active, suspended, terminated, cancelled, fraud |
next_due_date, billing_cycle | string | Next due date (YYYY-MM-DD) and billing cycle. |
capabilities | array | Available actions. VDS: status, start, stop, restart, cancel; hosting and game: cancel. |
hostname, ip, ips | string, string, array | Single service response only. |
Your services, 100 per page. Pass the last id as after for the next page.
Query parameters
| Field | Type | Required | Description |
|---|
after | integer | No | Last 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"
]
}
]
}
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"
}
| Field | Type | Description |
|---|
service_id | integer | Service ID. |
hostname, ip, ips | string, string, array | Service host name and IP addresses. |
username, password | string | Login details. |
server_hostname, server_ip | string | Hosting: 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
| Field | Type | Required | Description |
|---|
type | string | Yes | end_of_period or immediate |
reason | string | No | Cancellation 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 accountBalance
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.
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, manageDomains
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
| Field | Type | Description |
|---|
id, domain, order_id | integer, string, integer | Domain record. |
end_customer_ref | string | The reference you sent when ordering. |
status | string | pending, active, expired, transferred_away, cancelled and others. |
registration_date, expiry_date, next_due_date | string | YYYY-MM-DD |
auto_renew | boolean | When false it is not renewed from your balance at expiry. |
nameservers | array | Name servers. |
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
}
]
}
Availability and the 1 year register price.
Query parameters
| Field | Type | Required | Description |
|---|
domain | string | Yes | Domain 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"
}
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
| Field | Type | Required | Description |
|---|
domain | string | Yes | Domain name. |
type | string | Yes | register or transfer |
years | integer | Yes | 1 to 10 |
end_customer_ref | string | Yes | Your own customer reference; no personal data. |
nameservers | array | No | 2 to 5 name servers. |
epp_code | string | No | Authorization (EPP) code for transfers. |
id_protection | boolean | No | ID protection. |
additional_fields | object | No | Extra 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"
}
Your domains; paginate with after as for services.
Query parameters
| Field | Type | Required | Description |
|---|
after | integer | No | Last 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"
]
}
]
}
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
| Field | Type | Required | Description |
|---|
nameservers | array | Yes | New 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
}
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>"
}
domains.writeIP allowlist
Turns the transfer lock on or off.
Request body
| Field | Type | Required | Description |
|---|
locked | boolean | Yes | true 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
| Field | Type | Required | Description |
|---|
enabled | boolean | Yes | true 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 notificationsWebhooks
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
| Header | Description |
|---|
X-AnkSOFT-Event | Event name. |
X-AnkSOFT-Delivery | Delivery ID (the body id). Use it to ignore duplicates. |
X-AnkSOFT-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")> |
Events
| Event | When | data fields |
|---|
service.created, service.suspended, service.unsuspended, service.terminated | Service created, suspended, unsuspended, terminated. | service_id, order_id, end_customer_ref, reason (suspended only) |
service.renewed | Service renewed. | service_id, end_customer_ref, invoice_id, next_due_date |
invoice.created | Invoice created. | invoice_id, total, due_date, services: [{service_id, end_customer_ref}] |
invoice.paid | Invoice paid. | invoice_id, total, services |
domain.registered, domain.transferred, domain.renewed | Domain registered, transferred, renewed. | domain_id, domain, end_customer_ref, expiry_date |
balance.low | Balance below the threshold (at most once a day). | balance, threshold, currency |
ping | Test 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_ keysSandbox 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:
| Scenario | How | Result |
|---|
| Successful order | Any order | The job becomes done within seconds. |
| Setup failure | hostname: sandbox-setup-fail.test | The job ends failed. |
| Suspended service | hostname: sandbox-suspended.test | The service is created as suspended. |
| Insufficient balance | Header X-Sandbox-Scenario: insufficient_balance | 402 insufficient_balance |
| Taken domain | A name with taken in the label, for example foo.sandbox-taken.test | available: 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 pricingLevels
Your tier depends on the amount paid in the last 30 days. Catalog and domain prices already include your discount.
| Tier | Discount | Paid 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 toolsDownloads
Connect without writing code using the ready modules, or build your own integration with the PHP library.
Quick setupWHMCS and WiseCP setup
WHMCS server module
Requirements: WHMCS 8.x, PHP 7.4+ with cURL, a reseller API key.
- Upload
modules/servers/anksoftbayi/ from the zip to your WHMCS root. - 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. - Create a server group with this server.
- 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".
- 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
- Upload the
modules folder from the zip to your WHMCS root. - Activate the AnkSOFT module under Setup > Products/Services > Domain Registrars and enter your API key.
- Give the key
domains.read and domains.write; add services.read to follow jobs. Add your WHMCS server's IP to the allowlist. - 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).
- Copy the
coremio folder from the zip into the WiseCP root: <wisecp>/coremio/modules/Servers/AnksoftBayi/AnksoftBayi.php. - 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. - 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).
- You set the sale price and billing cycles on the WiseCP product. Enable only 1, 3, 6, 12, 24 and 36 month cycles.
FAQFrequently 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.