Overview #
All endpoints are REST calls with JSON responses. A successful response always has "success": true and the payload in the "data" object; an error has "success": false and an "error" message.
Base URL
All requests are sent over HTTPS.
https://api.resellerunion.com/v1/
Use the exact address above
Send every request directly to the address shown — over HTTPS, without a
www. prefix and without any other host variant. Under the HTTP
standard (RFC 7231) most HTTP clients turn a POST into a
GET when they follow a 301 redirect, so a request
that reaches the API through a redirect arrives as GET and the API answers
Method not allowed. Use POST.
The same applies to the sandbox. If your client follows redirects, disable
that option and use the exact address from the start.
Where do the parameter values come from?
Everything you send in orders comes from the catalog endpoints: extensions, domain prices and the
additional fields an extension requires from
price_list.php, hosting packages (their
uuid and available cycles) from
hosting_offer.php. Existing services and their
uuid identifiers come from
my_domains.php and
my_hosting.php.
Identification by UUID only
All services (domains, hosting) are identified in API calls by the public uuid identifier you receive in the responses. Internal numeric identifiers do not exist at the API boundary.
Authentication #
Every request is authenticated with two headers that always go together as a pair:
| Header | Description |
X-API-UUID |
The UUID of your reseller account. Visible in the control panel. |
X-API-KEY |
Your personal reseller API key; it can be regenerated at any time. |
GET https://api.resellerunion.com/v1/price_list.php
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
Exception: domain check
Credentials are optional only for
whois.php. If you do send them, they still go as a pair — sending only one of the two headers returns
401.
Security notes
All API calls must be made from server-side code. Never expose X-API-KEY in frontend/JavaScript code. The system logs every access attempt with an invalid key.
Errors and limits #
| HTTP code | Meaning |
| 400 | Invalid input (missing field, wrong format) or a business error (e.g. domain taken, cycle unavailable, extension not in the offer) |
| 401 | Missing or invalid credentials (X-API-UUID / X-API-KEY) |
| 405 | Wrong HTTP method for the endpoint |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
| 502 | Backend system temporarily unavailable — try again |
Rate limits (per IP address)
Domain check (whois.php): 60 requests per minute. All action POST endpoints (orders, transfers, renewals, package changes, service management, EPP, nameservers, contacts, registrant change): 30 requests per minute per IP address. Exceeding the limit returns 429.
// Error example
{
"success": false,
"error": "Field \"package_uuid\" is required."
}
Sandbox #
A test environment for developing your integration. Same paths and same response format as production, but without any real data and without consequences.
Same paths as production — just replace the base address. The list of available endpoints is further down on this page.
Credentials
They are public and the same for everyone — you do not need an account:
X-API-UUID: 00000000-0000-4000-8000-000000000001
X-API-KEY: sandbox_kljuc_za_testiranje_1234567
How it behaves
| Behaviour | Description |
| Stateless | The sandbox remembers nothing between requests. |
| Orders | order_* return success, but the order is not executed. No proforma invoice is created and nothing is charged. |
| Renewals and management | renew_*, manage_*, upgrade_hosting, transfer_domain, contacts and change_registrant validate input and status like production and return a response of the same shape, but change nothing. Proforma invoices in the response are simulated. |
| Lists | my_* always return the same fixed list. An ordered item will not appear in it. |
| Validation | Identical to production — you can also test failure paths (invalid uuid, private IP address, unsupported cycle, non-existent package). |
| Price level | The sandbox account is on level 2 (Advanced). |
The sandbox cannot be mistaken for production
Every response carries "sandbox": true and a sandbox_napomena field, and order responses additionally carry sandbox_upozorenje. The HTTP headers include X-Sandbox: true.
Example
POST https://sandbox.resellerunion.com/v1/order_license.php
Content-Type: application/json; charset=utf-8
X-API-UUID: 00000000-0000-4000-8000-000000000001
X-API-KEY: sandbox_kljuc_za_testiranje_1234567
{
"package_uuid": "aaaaaaaa-0000-4000-8000-000000000001",
"cycle": "monthly",
"ip": "203.0.113.50"
}
// Response
{
"success": true,
"sandbox": true,
"sandbox_napomena": "Test environment (sandbox). The data is fictitious…",
"data": {
"sandbox_upozorenje": "SANDBOX: the order was simulated and NOT executed…",
"proforma_invoice": { "number": "SB-2026-04128", "total": "61.51" },
"order": { "order_number": "RSL-4820193756", "status": 1 },
"license": { "name": "cPanel Admin Cloud", "ip": "203.0.113.50", "status": 1 }
}
}
Available endpoints
| Method | Path |
| GET | /v1/price_list.php |
| GET | /v1/whois.php |
| POST | /v1/order_domain.php |
| POST | /v1/transfer_domain.php |
| POST | /v1/renew_domain.php |
| POST | /v1/epp_code.php |
| GET POST | /v1/nameservers.php |
| GET POST | /v1/contacts.php |
| GET POST | /v1/change_registrant.php |
| GET | /v1/my_domains.php |
| GET | /v1/hosting_offer.php |
| POST | /v1/order_hosting.php |
| GET | /v1/my_hosting.php |
| POST | /v1/renew_hosting.php |
| POST | /v1/upgrade_hosting.php |
| POST | /v1/manage_hosting.php |
| GET | /v1/license_offer.php |
| POST | /v1/order_license.php |
| GET | /v1/my_licenses.php |
| POST | /v1/renew_license.php |
| POST | /v1/manage_license.php |
| GET | /v1/dedicated_offer.php |
| POST | /v1/order_dedicated.php |
| GET | /v1/my_dedicated.php |
| POST | /v1/renew_dedicated.php |
| POST | /v1/manage_dedicated.php |
| GET | /v1/my_profile.php |
| GET | /v1/credit.php |
| GET | /v1/my_documents.php |
| GET POST | /v1/settings.php |
Testing the WHMCS modules against the sandbox
The API base is fixed inside the modules. For testing, temporarily change the resellerba_*_apiBase() function so that it returns https://sandbox.resellerunion.com/v1 — and change it back before going to production.
Domain price list #
Returns all domain extensions offered on your platform with registration and renewal prices for your price level, plus the currency. This is the source of the values for domain registration and transfer, as well as of the additional fields that some extensions require.
The optional query parameter ?lang=en|es|de|fr sets the language of the names and descriptions of the additional fields. Without it, the platform's default language is used.
Example request
GET https://api.resellerunion.com/v1/price_list.php
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
Response
{
"success": true,
"data": {
"level": 2,
"currency": "EUR",
"decimal_separator": ".",
"total_count": 42,
"domains": [
{
"extension": ".com",
"featured": true,
"registration_price": "12.90",
"renewal_price": "14.90",
"registration_periods": {
"1": "12.90",
"2": "25.80",
"3": "38.70",
"4": "51.60",
"5": "64.50"
},
"custom_fields": []
},
{
"extension": ".us",
"featured": false,
"registration_price": "9.90",
"renewal_price": "9.90",
"registration_periods": {
"1": "9.90",
"2": "19.00",
"5": "45.00"
},
"custom_fields": [
{
"slot": 1,
"required": true,
"type": "dropdown",
"name": "Nexus category",
"description": "The registrant's connection to the United States, as required by the .us registry.",
"options": ["C11", "C12", "C21", "C31", "C32"],
"validation": "^(C11|C12|C21|C31|C32)$"
},
{
"slot": 2,
"required": true,
"type": "dropdown",
"name": "Application purpose",
"description": "The intended use of the domain, as required by the .us registry.",
"options": ["P1", "P2", "P3", "P4", "P5"],
"validation": "^(P1|P2|P3|P4|P5)$"
}
]
}
]
}
}
Registration periods per extension
The
registration_periods object contains
only the periods offered for that extension — the key is the number of years, the value is the total price for the whole period. A period missing from the object cannot be ordered (in the example above,
.us offers 1, 2 and 5 years; 3 and 4 are not available). The
period value sent at
domain registration must be one of these keys.
Additional fields per extension
The
custom_fields array is filled
only for extensions that require additional fields; for all others it is empty. Every listed field is
required — without it
domain registration returns an error.
slot is the ordinal number (1–6) and is sent back as the key in the
custom_fields object when ordering. The name is display text and may change; the slot never changes — bind to the slot.
type can be
check (checkbox; value
"true"),
input (free text) or
dropdown. A dropdown also comes with an
options array of allowed values.
validation is a regular expression
without delimiters, the same one the server uses to check the value. It is sent so that you can validate the input before submitting and show the user an error immediately.
Hosting offer #
Returns all hosting packages offered on your platform with prices for your price level across all available billing cycles. From this response you take the package uuid and the cycle name for the hosting order.
Example request
GET https://api.resellerunion.com/v1/hosting_offer.php
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
Response (abbreviated)
{
"success": true,
"data": {
"level": 2,
"currency": "EUR",
"total_count": 6,
"packages": [
{
"uuid": "3f2a1b6c-9d4e-4f70-8a21-5e6d7c8b9a01",
"name": "Business",
"disk_space": "10 GB",
"bandwidth": "Unlimited",
"requires_domain": true,
"prices": {
"monthly": "9.90",
"annually": "99.00"
}
}
]
}
}
A cycle that is not in the response is not offered
The prices object of each package contains only the cycles available for that package and your level. Sending an order with a cycle that is not in the package's offer returns an error — a price is never made up.
Check domain availability #
Checks whether the requested domain is available for registration. Only extensions that are in the reseller offer for your platform are supported (see the price list). Trying to check an extension outside the offer returns an error.
GET parameters
| Parameter | Type | Required | Description |
domain |
string |
YES |
Full domain name including the extension (e.g. example.com, example.co.uk) |
format |
string |
NO |
text = plain-text response for WHMCS (see below). Without the parameter: JSON. |
Credentials are optional
If you send credentials, X-API-UUID and X-API-KEY go together as a pair — sending only one returns 401.
Example request
GET https://api.resellerunion.com/v1/whois.php?domain=example.com
Response
{
"success": true,
"data": {
"domain": "example.com",
"extension": ".com",
"available": true
}
}
Text format (WHMCS whois.json)
With the format=text parameter the endpoint returns plain text — intended for the WHMCS whois.json "https://" lookup. The response is always HTTP 200 (WHMCS compares the body, not the HTTP code):
STATUS: AVAILABLE
STATUS: REGISTERED
STATUS: ERROR: <message>
Example whois.json entry. The same entry works for any extension in the offer, including extensions that have no public port-43 whois server — list the extensions you need:
{
"extensions": ".com,.net",
"uri": "https://api.resellerunion.com/v1/whois.php?format=text&domain=",
"available": "STATUS: AVAILABLE"
}
Register domain #
Creates a domain registration order. The domain must be available (the system performs the check) and the extension must be in your platform's offer. The price is calculated according to your price level.
POST JSON fields
| Field | Type | Required | Description |
domain | string | YES | Full domain name (e.g. example.us) |
period | integer | YES | Number of registration years: 1–5. Only a period that exists in registration_periods for that extension in the price list is allowed — every period has its own total price. An unavailable period returns an error. |
registrant | object | YES | Registrant details — see Registrant details |
nameservers | array | YES | At least 2, at most 5 hostnames. Each must be different. The registry rejects a domain with fewer than two nameservers, so an order without them is not accepted. |
custom_fields | object | * | Required if the extension requires additional fields — see custom_fields in the price list. The key is the slot as a string, the value is text. |
Example request
POST https://api.resellerunion.com/v1/order_domain.php
Content-Type: application/json; charset=utf-8
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
{
"domain": "example.us",
"period": 1,
"registrant": {
"is_company": 0,
"first_name": "John",
"last_name": "Smith",
"address": "123 Main Street",
"city": "Springfield",
"postal_code": "62701",
"country": "US",
"email": "john@example.com",
"phone": "+12025550142"
},
"nameservers": ["ns1.example.com", "ns2.example.com"],
"custom_fields": {
"1": "C11",
"2": "P3"
}
}
Nameservers are required
At least two are needed and they must be different. The check is done before the order and the proforma invoice are created, so an order without them never comes into existence and nothing is charged. Default nameservers are not substituted — a nameserver you did not specify is never made up.
Additional fields
Before ordering, read the
custom_fields for that extension from
price_list.php. Every active field is required; the value must satisfy the
validation regex and, for a dropdown, be one of the values in the
options array.
For a field of type
check the text
"true" is sent (or JSON
true, which the server converts to the same thing).
A value sent for a slot that the extension
does not require is rejected with an error — that is a sign you are working from an outdated field definition.
Response
The response is nested: data contains four objects — order, domain, proforma_invoice and currency. The order UUID is data.order.uuid and the domain UUID is data.domain.uuid — not data.uuid.
{
"success": true,
"data": {
"order": {
"uuid": "945bda1a-2a36-4f7e-acfd-4c3cfd158a99",
"order_number": "RSL-3430439926",
"status": 1
},
"domain": {
"uuid": "4dd69549-68df-4cf7-9bd5-dc73b38acc8d",
"domain": "example.us",
"extension": ".us",
"registration_period": 1,
"level": 2,
"price": "58.67",
"status": 1,
"nameservers": ["ns1.example.com", "ns2.example.com"],
"custom_fields": {
"1": "C11",
"2": "P3"
}
},
"proforma_invoice": {
"proforma_number": "1564",
"subtotal": "50.15",
"vat_amount": "8.52",
"total": "58.67",
"currency": "€",
"status": "unpaid"
},
"currency": {
"code": "EUR",
"prefix": "",
"suffix": "€",
"position": "after",
"space": 1,
"rate": "1.000000"
},
"decimal_separator": "."
}
}
Common mistake
data.uuid does not exist. There are
data.order.uuid (the order) and
data.domain.uuid (the domain) — two different objects and two different UUIDs.
To track the domain through
my_domains.php use
data.domain.uuid.
Currency always comes from the response
The currency code, symbol and position are returned in the currency object of every
order response and in the catalog endpoints. Never assume them in your integration — read them
from the response.
custom_fields
Present only when the extension requires additional fields. The key is the
slot as a string,
the same one returned by
price_list.php.
Transfer domain #
Creates a transfer order for a domain to your account. The domain must be registered (for a free domain use registration). The transfer price is the registration price for your level and includes one year of renewal.
POST JSON fields
| Field | Type | Required | Description |
domain | string | YES | Full domain name |
period | integer | YES | Only 1 |
eppcode | string | YES | EPP / authInfo code from the current registrar |
registrant | object | YES | See Registrant details |
nameservers | array | NO | Without them the domain keeps its current nameservers. If sent: at least 2, at most 5 |
custom_fields | object | depends on extension | Same as for registration |
Flow
After payment the transfer is started at the registry and the domain gets status 2 (transfer pending). When the current registrar confirms the transfer, the domain moves to status 3 (active).
Renew domain #
Creates a proforma invoice to renew the domain for the period it is registered for. The domain is identified by its uuid from my_domains.php. With automatic renewal enabled and enough credit, the proforma invoice is charged from credit and the domain is renewed at the registry (within a few minutes).
POST https://api.resellerunion.com/v1/renew_domain.php
Content-Type: application/json; charset=utf-8
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
{
"uuid": "9c1f4a2e-7b3d-4e58-a690-1f2e3d4c5b6a"
}
Response
{
"success": true,
"data": {
"proforma_invoice": { "proforma_number": "1043", "total": "23.00", "currency": "€", "status": "unpaid" },
"domain": {
"uuid": "9c1f4a2e-…", "domain": "example.com", "period_years": 1,
"post_expiry_period": "regular", "renewal_price": "23.00",
"grace_fee": "0.00", "redemption_fee": "0.00",
"expires_at": "2026-10-02", "new_expires_at": "2027-10-02"
},
"auto_renewal": { "enabled": true, "message": "…" }
}
}
post_expiry_period: regular, grace or redemption. In the grace and redemption periods the fee is a separate proforma item. A deleted domain (status 10), a suspended one and a pending one cannot be renewed (409). If an unpaid renewal proforma invoice already exists for the domain, the response is 409 with its number.
EPP code #
Sends the domain's EPP (authInfo) code to the registrant's email. The domain is identified by its uuid. For security reasons the code is never returned in the API response.
{
"uuid": "9c1f4a2e-7b3d-4e58-a690-1f2e3d4c5b6a"
}
Domain nameservers #
GET ?uuid=<uuid> returns the domain's current nameservers. POST changes the nameservers at the registry.
POST JSON fields
| Field | Type | Required | Description |
uuid | string | YES | Domain UUID |
nameservers | array | YES | List of up to 5 hostnames |
{
"uuid": "9c1f4a2e-7b3d-4e58-a690-1f2e3d4c5b6a",
"nameservers": ["ns1.example.com", "ns2.example.com"]
}
Change of domain owner (registrant) #
This is charged. A change of owner means a change of the company name (registrant is a company) or of the first and last name (registrant is an individual). The other details of the new owner are sent along with it. Changing only the address, email, phone and similar is free and goes through contacts.php — this endpoint rejects it.
Available only for extensions that support an owner change and have a change price for your level; for the others the response is 409.
GET ?uuid=<domain uuid>
Returns the current registrant (from the registry), the change price (change_price) and billable_change: company or full_name — the field that has to change. If a request is already waiting for payment, it returns pending_request: true and that request's data.
POST JSON fields
| Field | Required | Description |
uuid | YES | Domain UUID |
company | company: YES | New company name — must differ from the current one |
first_name, last_name | individual: YES | New first and last name — must differ from the current ones. For a company: contact person (optional) |
address, city, postal_code, state, country | YES (except postal_code, state) | New owner's address (country = ISO-2) |
phone, email | YES | New owner's contact |
POST creates the request and a proforma invoice. After payment the change is applied at the registry automatically. An unpaid request is cancelled with {"uuid": "…", "action": "cancel"}.
Order hosting #
Creates a hosting package order and the corresponding proforma invoice. You take the package and the cycle from hosting_offer.php — the order goes through only for a cycle that is in that package's offer.
POST JSON fields
| Field | Type | Required | Description |
package_uuid | string | YES | Package uuid from the hosting offer |
cycle | string | YES | Cycle name — see Billing cycles |
domain | string | NO* | The service's domain. Required if the package has requires_domain: true. |
Example request
POST https://api.resellerunion.com/v1/order_hosting.php
Content-Type: application/json; charset=utf-8
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
{
"package_uuid": "3f2a1b6c-9d4e-4f70-8a21-5e6d7c8b9a01",
"cycle": "annually",
"domain": "example.com"
}
Response
The response is nested: data contains order, service, proforma_invoice, auto_activation and currency. The service UUID is data.service.uuid.
{
"success": true,
"data": {
"order": {
"uuid": "e7a0565d-a746-456e-8938-68412977e573",
"order_number": "RSL-3036365111",
"status": 1
},
"service": {
"uuid": "c3c3b3f0-db2f-42cc-a195-99e6a97aedb8",
"package": "START HOSTING",
"cycle": "annually",
"cycle_months": 12,
"domain": "example.com",
"level": 1,
"price": "54.40",
"status": 1,
"dedicated_ip": false,
"package_price": "54.40",
"dedicated_ip_price": null
},
"proforma_invoice": {
"proforma_number": "1564",
"subtotal": "46.50",
"vat_amount": "7.90",
"total": "54.40",
"currency": "€",
"status": "unpaid"
},
"auto_activation": {
"enabled": true,
"message": "Automatic activation: the available credit covers the proforma invoice amount."
},
"currency": {
"prefix": "",
"suffix": "€",
"position": "after",
"space": 1
},
"decimal_separator": "."
}
}
Common mistake
data.uuid does not exist. The service UUID is data.service.uuid and the
order UUID is data.order.uuid.
Dedicated IP
When ordered, price INCLUDES it — the document has one line item, not two.
package_price and dedicated_ip_price are informative split amounts.
My hosting services #
List of your hosting services. Parameters: ?limit=50&offset=0 (optional) or ?uuid=<uuid> for a single service. For each service it returns: uuid, package, domain, status, price, cycle, dates, disk/bandwidth usage and the order number.
| Service status | Meaning |
1 | Pending |
2 | In progress |
3 | Active |
License offer #
The complete license offer with prices according to your price level. Licenses are grouped into categories, and each carries a uuid that you use when ordering.
Example request
GET https://api.resellerunion.com/v1/license_offer.php
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
Response (abbreviated)
{
"success": true,
"data": {
"level": 2,
"currency": { "code": "EUR", "suffix": "€", "position": "after", "space": 1 },
"decimal_separator": ".",
"categories": [ { "name": "cPanel/WHM" }, { "name": "CloudLinux" } ],
"total_count": 24,
"licenses": [
{
"uuid": "3f2a1b6c-9d4e-4f70-8a21-5e6d7c8b9a01",
"name": "cPanel Admin Cloud",
"category": "cPanel/WHM",
"host_type": "virtual",
"max_accounts": 5,
"term_months": 1,
"featured": true,
"prices": { "monthly": "61.51" }
}
]
}
}
| License field | Type | Description |
uuid | string | Identifier used when ordering |
name | string | License name |
category | string | Group the license belongs to |
host_type | string | virtual or dedicated — the server type the license is valid for |
max_accounts | int|null | Maximum number of accounts; null = unlimited |
prices.monthly | string | Price for your level, per month |
Licenses are monthly only
A license is leased for one month and renewed every month. Longer cycles do not exist — the cycle field at ordering accepts only monthly.
Order license #
Creates a license order and the corresponding proforma invoice. The license is bound to the server IP address you send in the request — without it the license cannot be activated.
POST JSON fields
| Field | Type | Required | Description |
package_uuid | string | YES | License uuid from the license offer |
cycle | string | YES | Always monthly — licenses are leased monthly only |
ip | string | YES | Public IPv4 or IPv6 address of the server the license is bound to |
IP address restrictions
The address must be public — private and reserved ranges (10.x, 192.168.x, 172.16–31.x, 127.x) are rejected with 400. A second license cannot go to the same IP address; the attempt returns 409.
Example request
POST https://api.resellerunion.com/v1/order_license.php
Content-Type: application/json; charset=utf-8
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
{
"package_uuid": "3f2a1b6c-9d4e-4f70-8a21-5e6d7c8b9a01",
"cycle": "monthly",
"ip": "203.0.113.50"
}
Response
{
"success": true,
"data": {
"proforma_invoice": { "number": "P-2026-00412", "total": "61.51", "currency": "€", "status": "unpaid" },
"order": { "uuid": "…", "order_number": "RSL-0000100042", "status": 1 },
"license": { "uuid": "…", "name": "cPanel Admin Cloud", "ip": "203.0.113.50", "status": 1 }
}
}
The license stays in the Pending status until the proforma invoice is paid. After payment it is activated and receives a licenseid — track its state through my_licenses.php.
My licenses #
List of your licenses. Parameters: ?limit=50&offset=0 (optional) or ?uuid=<uuid> for a single license.
| Field | Type | Description |
uuid | string | License identifier |
name | string | License name at the time of purchase |
category | string | Group the license belongs to |
status | int | See the status table below |
greska | string|null | Failure reason — filled only for status 4 |
ip | string | IP address the license is bound to |
licenseid | string|null | License number at the vendor; null until activated |
hostname | string|null | Server hostname, if known |
price | string | Monthly price at which it was sold |
activated_at | string|null | Date and time of activation |
expires_at | string|null | Expiry date of the current month |
order_number | string|null | Number of the order with which the license was purchased |
| License status | Meaning |
1 | Pending — order received, awaiting payment |
3 | Active |
4 | Failed — reason in the greska field |
5 | Expired |
7 | Cancelled |
9 | Suspended |
Renew hosting service #
Creates a proforma invoice for the service's next period. The service is renewed with the cycle it already has — the cycle does not change on renewal.
POST JSON fields
The price is not sent — the system determines it according to your price level and the cycle the service has.
Example request
POST https://api.resellerunion.com/v1/renew_hosting.php
Content-Type: application/json; charset=utf-8
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
{
"uuid": "7c1e9a2b-4d38-4f61-9b02-1a5c8e7d3f40"
}
Response
{
"success": true,
"data": {
"proforma_invoice": { "proforma_number": "P-2026-00518", "total": "19.56", "currency": "€", "status": "unpaid" },
"service": {
"uuid": "7c1e9a2b-…", "package": "BUSINESS HOSTING", "domain": "example.com",
"cycle_months": 1, "expires_at": "2026-08-20", "new_period_until": "2026-09-19"
},
"auto_activation": { "enabled": true, "message": "Automatic renewal: the available credit…" }
}
}
When renewal is possible
| Service state | Renewal |
| Active | Possible |
| Expired, within the renewal period | Possible |
| Expired, renewal period over | 409 — reactivation required |
| Suspended | 409 — reactivate the service first |
| Cancelled | 409 |
| Pending (not yet activated) | 409 |
Existing unpaid proforma invoice
If an unpaid renewal proforma invoice already exists for the service, a new one is not created. The response is 409 with the number of the existing proforma invoice — pay that one or wait for it to be processed.
Change hosting package #
Moves the service to another package, keeping the same billing cycle. The unused part of the current period counts as credit, the new package is charged for the remaining days, and the difference is the amount due.
| Field | Type | Required | Description |
uuid | string | YES | Service uuid |
package_uuid | string | YES | New package uuid from hosting_offer.php |
| Case | Result |
| Amount due above 0 (bigger package) | A proforma invoice for the difference; the package changes after payment (with auto_activation and enough credit — automatically). changed: false, calculation holds the breakdown. |
| Amount due 0 (smaller or equal package) | The package changes immediately, with no proforma invoice and no refund of the difference. changed: true. |
A service with a dedicated IP keeps the IP; the new package must offer an IP for your level and cycle. If the server rejects the new package (e.g. more disk used than the new package allows), the service stays on the old package.
Manage hosting service #
Suspends, unsuspends or terminates the hosting account on the server.
| Field | Type | Required | Description |
uuid | string | YES | Service uuid |
action | string | YES | suspend, unsuspend or terminate |
confirm | boolean | for terminate | true — termination removes the account and is irreversible |
A suspended service is not renewed automatically — that is how a WHMCS suspension stops the renewal (see Renewals).
Renew license #
Creates a proforma invoice for the license's next month. Licenses are leased and renewed monthly only.
POST JSON fields
| Field | Type | Required | Description |
uuid | string | YES | License uuid from My licenses |
Example request
POST https://api.resellerunion.com/v1/renew_license.php
Content-Type: application/json; charset=utf-8
X-API-UUID: 550e8400-e29b-41d4-a716-446655440000
X-API-KEY: your_api_key
{
"uuid": "9a4f2c81-6b30-4e75-8d19-3f7b2e5a1c68"
}
The response has the same shape as for hosting, with a license block instead of service. The same rules apply to the states in which renewal is not possible, as well as to an existing unpaid proforma invoice.
Terminate license #
Permanently cancels the license at cPanel. Licenses are not suspended — they are billed for as long as they exist, so a license your client no longer uses is terminated. An unpaid renewal proforma invoice for that license is cancelled.
| Field | Type | Required | Description |
uuid | string | YES | License uuid from my_licenses.php |
action | string | YES | terminate |
confirm | boolean | YES | true |
Renewals, deadlines and expiry #
Your account has two separate settings (reseller panel, Settings; API: settings.php):
| Setting | Applies to | When enabled |
auto_activation | New orders and package changes | The proforma invoice is charged from credit immediately and the service is activated |
auto_renewal | Renewal of existing services | The renewal proforma invoice is charged from credit and the service is renewed; only the invoice is emailed to you |
If there is not enough credit, the proforma invoice stays unpaid and waits for a payment or a credit top-up — nothing is ever charged partially.
Automatic renewal OFF
For domains, hosting, licenses and dedicated servers the renewal proforma invoice is issued in advance, before expiry (the number of days depends on the billing cycle) and emailed to you. The service is renewed as soon as it is paid. You can also start a renewal yourself at any time with the renew endpoints.
Automatic renewal ON
| Service | When it is renewed |
| Domain | Only on your renew_domain.php call (WHMCS sends it on its own). The proforma invoice is charged from credit and the domain is renewed at the registry. |
| Hosting | hosting_grace days after expiry, unless the service has been suspended or terminated |
| Dedicated server | hosting_grace days after expiry, unless the server has been suspended or terminated |
| License | licence_grace days after expiry, unless the license has been terminated |
Why hosting, servers and licenses renew after expiry
WHMCS provisioning modules do not send a renewal command for hosting and licenses, but they do send suspension and termination. The
hosting_grace /
licence_grace window (see
my_profile.php) is your room: if your client does not pay and you suspend or terminate the service, no renewal happens and no credit is taken. If you do nothing, the service is renewed.
You can always renew manually with renew_hosting.php, renew_dedicated.php or renew_license.php, or from the WHMCS module.
New period after renewal
Domain: old expiry date + number of years. Hosting, licenses and servers: old expiry date + billing cycle, without a gap — paying a few days after expiry does not shift the date. If the service stayed expired for longer than a whole cycle, the new period starts on the payment date.
What happens to a service that is not renewed
| Service | After expiry |
| Hosting | More than hosting_grace days: suspension. Day 60: termination (the account is removed from the server) and the unpaid proforma invoice is cancelled. Paying before termination lifts the suspension and renews the service. |
| Dedicated server | More than hosting_grace days: suspension (network disabled). Day 60: termination. |
| License | Day 1: status expired. More than licence_grace days: termination at cPanel — licenses are not suspended, because they are billed for as long as they exist. |
| Domain | Grace period, then redemption period (days per extension — see the price list); a renewal in that time also includes the grace or redemption fee as a separate proforma item. On day 60 the domain becomes deleted and can no longer be renewed. |
My domains #
List of your domains. Parameters: ?limit=50&offset=0 (optional). For each domain it returns: uuid, name, status, price, dates and the order number — the uuid from here is what you use for renewal, EPP, nameservers and contacts.
| Domain status | Meaning |
1 | Pending |
3 | Active |
4 | Failed |
5 | Expired |
Dedicated server offer #
The complete dedicated server offer with prices for your price level,
together with the available configurable options.
Servers are leased monthly only
No longer cycle is offered. The prices block contains only monthly.
Response
The response has two parts: packages (servers) and options
(additional options chosen when ordering).
{
"success": true,
"data": {
"level": 2,
"currency": { "code": "EUR", "suffix": "€", "position": "after" },
"decimal_separator": ".",
"total_count": 11,
"packages": [
{
"uuid": "3f1c8a20-68df-4cf7-9bd5-dc73b38acc8d",
"name": "2 x 18 Core Xeon Gold 6254",
"subtitle": "DELL PowerEdge R640",
"featured": false,
"sold_out": false,
"specification": {
"model": "DELL PowerEdge R640",
"cpu": "Xeon Gold 6254",
"cpu_count": 2,
"cores": 36,
"cpu_speed": "32 x 3.10 GHz",
"hyperthreading": true,
"ram_included": "64 GB",
"ram_type": "DDR 4",
"disks_included": [
{ "name": "960 GB SSD Enterprise", "count": 2 }
],
"drive_bays": 8,
"included_ip_addresses": 1
},
"available_options": {
"ram": ["ram-1", "ram-2", "ram-3"],
"raid": ["raid-1", "raid-2"],
"disk_slots": [
["disk-6", "disk-7"],
["disk-6", "disk-7"]
]
},
"prices": { "monthly": "799.20" }
}
],
"options": {
"ram": [
{
"code": "ram-2",
"name": "Upgrade to 128 GB",
"free": false,
"prices": { "monthly": "96.00" }
}
],
"disks": [ ... ],
"internet": [ ... ],
"management": [ ... ],
"cpanel": [ ... ],
"raid": [ ... ],
"ip": [ ... ],
"operating_systems": [ ... ]
}
}
}
How to link a package and its options
The options block lists all options on the platform.
available_options inside a package says which of them that
package offers — matching is done by the code field.
disk_slots is an array of arrays: one entry per physical slot, because
the disk is chosen separately for each slot.
What is already included in the price
ram_included and disks_included describe the configuration that
comes with the server. The options from options are upgrades
and are charged additionally.
| Field | Type | Description |
uuid | string | Package identifier — used when ordering |
name | string | Server name |
subtitle | string | Short description, usually the chassis model |
featured | bool | Highlighted in the offer |
sold_out | bool | Currently unavailable |
specification | object | Hardware description; see the example above |
available_options | object | Codes of the options this package offers |
prices.monthly | string | Your monthly price for your level |
Dedicated server order #
Orders a dedicated server with the chosen configuration. Creates an order and
a proforma invoice; the server stays in status 1 until delivery.
Delivery is manual
A dedicated server is a physical machine and is not activated automatically. After
payment the server waits for the technical team to put it into operation; the IP
address and root access appear only then.
Auto-activation from credit
When automatic activation is enabled on the account, the proforma invoice is charged
from your credit immediately: an invoice is issued, the old advance invoice
is reversed and a new one is issued for the remaining amount — all automatically,
with the documents sent by e-mail. Even then the server waits for manual delivery
(the status stays 1). Without enough credit the proforma invoice stays
unpaid and waits for a payment.
Request
{
"package_uuid": "dddddddd-0000-4000-8000-000000000002",
"cycle": "monthly",
"hostname": "srv1.example.com",
"options": ["ram-2", "raid-2", "cp-1", "os-1"],
"disks": ["disk-7", "disk-6"],
"extra_ip": 4
}
| Field | Required | Description |
package_uuid | yes | uuid from dedicated_offer.php |
cycle | yes | Always monthly — servers are leased monthly |
hostname | no | Desired hostname; can also be set later |
options | no | Upgrade codes: memory, RAID, link, management, control panel, OS |
disks | no | Disk codes per slot, in order |
extra_ip | no | Number of additional IP addresses (0 if none are ordered) |
An option the package does not offer is an error
A code that is not listed in that package's available_options returns
400 — it is not skipped silently. The same applies to a disk in a slot
that does not support it and to two options of the same kind.
Disks go per slot
disks is an array where the first element belongs to the first slot, the
second to the second and so on. An empty slot is sent as an empty string:
"disks": ["disk-7", "", "disk-6"]
The number of elements must not exceed the number of slots from
specification.drive_bays.
Response
{
"success": true,
"data": {
"order": {
"uuid": "7c2f9a10-4e88-4a31-b0d2-1f5e77c3a904",
"order_number": "RSL-4839947402",
"status": 1
},
"server": {
"uuid": "b41d7e60-9c2a-4f18-8ad5-3e90b21c7f56",
"name": "2 x 18 Core Xeon Gold 6254",
"model": "DELL PowerEdge R640",
"hostname": "srv1.example.com",
"cycle": "monthly",
"cycle_months": 1,
"level": 2,
"price": "1044.06",
"package_price": "849.15",
"options_price": "194.91",
"selected_options": ["Upgrade to 128 GB", "RAID 10"],
"ordered_ip_addresses": 4,
"status": 1
},
"proforma_invoice": {
"number": "1587",
"subtotal": "892.36",
"vat_amount": "151.70",
"total": "1044.06",
"currency": "€",
"status": "unpaid"
},
"currency": { "prefix": "", "suffix": "€", "position": "after" },
"decimal_separator": "."
}
}
Common mistake
data.uuid does not exist. The order UUID is
data.order.uuid
and the server UUID is
data.server.uuid — for tracking through
my_dedicated.php the latter is used.
| Code | Error |
400 | Invalid option code or an option the package does not offer |
400 | Disk in a slot that does not support it, or more disks than slots |
400 | Two options of the same kind (e.g. two memory upgrades) |
400 | More IP addresses than allowed |
404 | Server not in the offer |
My dedicated servers #
List of your servers. Parameters: ?limit=50&offset=0 (optional)
or ?uuid=<uuid> for a single server.
Delivery is manual
A dedicated server is a physical machine and is not activated automatically. After
payment the server stays in status 1 until the technical team puts it
into operation. Root access and IP addresses appear only then.
| Field | Type | Description |
uuid | string | Server identifier |
name | string | Package name at the time of purchase |
model | string | Machine model (e.g. DELL PowerEdge R640) |
hostname | string | Hostname, if set |
status | int | See the status table below |
level | int | Your price level at the time of purchase |
cycle | string | Always monthly |
price | string | Total monthly price, with the selected options |
package_price | string | Server only, without options |
options_price | string | Sum of the selected options |
main_ip | string | Main IP address; empty until the server is delivered |
main_ipv6 | string | Main IPv6 address, if assigned |
ordered_ip_addresses | int | Number of additional IP addresses ordered |
leased_ip_addresses | int | Number of additional IP addresses actually assigned |
root_username | string | Root username |
specification | object | Hardware description |
configuration | object | Selected options with the price at which they were charged |
leased_at | string|null | Lease date |
expires_at | string|null | Expiry date of the current month |
terminated_at | string|null | Termination date, if the service was terminated |
Prices in configuration are frozen
They show the amount that applied at the time of the order, not the current price list.
The price list may change, but you are charged what was agreed.
| Server status | Meaning |
1 | Awaiting delivery — payment received, the server is being prepared |
2 | Active |
3 | Suspended |
4 | Terminated |
5 | Cancelled |
6 | Fraud |
Renew dedicated server #
Creates a proforma invoice for the next month of the server, at the server's contracted price (package + options chosen at order time).
| Field | Type | Required | Description |
uuid | string | YES | Server uuid from My servers |
Renewal is possible for an active server and for a server expired within hosting_grace. A suspended, terminated or cancelled server returns 409, as does an existing unpaid renewal proforma invoice. The response has the same shape as renew_hosting.php (with hostname instead of package and domain).
Manage dedicated server #
Reboot, network, suspension and termination of the server. Executed through DCIM; the status changes only after DCIM confirms the action.
| Field | Type | Required | Description |
uuid | string | YES | Server uuid |
action | string | YES | reboot, network_on, network_off, suspend, unsuspend, terminate |
confirm | boolean | for terminate | true — termination is irreversible |
On a suspended server only unsuspend and terminate are allowed. The response returns service.status and the network state (network: on / off).
My documents #
The latest proforma invoices and invoices. Parameter: ?limit=10 (1–50, default 10). Each document contains uuid, number, dates, amount, currency and status. Proforma invoice status: 0 = unpaid, 1 = paid.
My profile #
Returns your profile data in the registrant format — ready for the "register with my details" option when ordering a domain.
Credit #
The current balance of your credit: { credit: { amount, currency, broj_avansnog_racuna, datum } }. Without an active advance payment the amount is 0.00.
Settings #
GET returns { settings: { auto_activation: 0|1, auto_renewal: 0|1 } }. POST { "auto_activation": 0|1, "auto_renewal": 0|1 } (one or both fields) changes the settings and returns the new state. Auto-activation means that services are activated automatically after payment; auto-renewal is described under Automatic renewal and activation.
Registrant details #
The registrant object sent at domain registration and transfer:
| Field | Required | Description |
is_company | YES | 0 = individual, 1 = company (legal entity) |
first_name, last_name | YES | First and last name of the registrant / contact person |
company | NO* | Company name — for companies |
company_id | NO* | Company registration number — for companies |
vat_id | NO | VAT number — for VAT-registered companies |
address, city, postal_code | YES | Registrant's address |
country | YES | ISO-2 country code (e.g. US, GB, DE) |
email, phone | YES | Registrant's contact details (phone in international format) |
Fields for companies
For is_company: 1 you must also send company and company_id. Incomplete data returns a business error describing the missing field.
Billing cycles #
The cycle field in a hosting order accepts one of the following values (string):
| Value | Period | Number of months |
monthly | Monthly | 1 |
quarterly | Quarterly | 3 |
semiannually | Semi-annually | 6 |
annually | Annually | 12 |
biennially | Every two years | 24 |
triennially | Every three years | 36 |
quadrennially | Every four years | 48 |
quinquennially | Every five years | 60 |
Availability per package
Which cycles a specific package offers is shown in that package's
prices object in the
hosting offer. A cycle that is not in the package's offer cannot be ordered.
Domains
Domains do not use cycles — the period is sent as a number of years in the
period field: registration
1–
5 years (availability per extension from
registration_periods in the
price list), transfer currently
1 year.
Licenses
Licenses are leased and renewed monthly only. The cycle field in a license order accepts only the value monthly — longer cycles do not exist and return 400.