Partner API
Every partner API call: list, issue, move and remove licences, change their products, bulk issue and remove, the paged fleet view, safe retries and errors.
The partner API lets your own systems manage your licences: issue one when you create a server, move it when a server gets a new IP address, and remove it when you delete the server. It answers JSON over HTTPS.
Address: https://api.hostfog.com/v1/licence/partner/…
Every call needs your partner API key in the Authorization header (see Get an API key).
Bodies of POST calls are JSON. A key sees and changes only its own partner account's licences.
Calls
| Method | Address | Body | Answer |
|---|---|---|---|
| GET | /v1/licence/partner/licences |
— | {"quantity", "used", "licences": [{"key", "email", "ip", "status", "products", "install"}]} |
| POST | /v1/licence/partner/issue |
{"email", "ip", "products"} |
{"key", "products", "install"} |
| POST | /v1/licence/partner/products |
{"key", "products"} |
{"key", "products"} |
| POST | /v1/licence/partner/move |
{"key", "ip"} |
{"key", "ip"} |
| POST | /v1/licence/partner/remove |
{"key"} |
{"removed"} |
| POST | /v1/licence/partner/issue-bulk |
{"servers": [{"email", "ip"}, …], "atomic"} |
{"issued", "failed", "results"} — one result per server |
| POST | /v1/licence/partner/remove-bulk |
{"keys": ["…", …], "atomic"} |
{"removed", "failed", "results"} — one result per key |
| GET | /v1/licence/partner/fleet |
— | {"total", "page", "per", "pages", "counts", "licences"} — one page of your servers with their state |
List your licences
curl -s https://api.hostfog.com/v1/licence/partner/licences \
-H "Authorization: Bearer hfp_…"
| Field | What it is |
|---|---|
quantity |
the licences in your pool (for usage billing: your agreed cap, 0 = none) |
used |
the licences issued now |
licences[].key |
the licence key, HFL-… |
licences[].email |
the customer email it was issued for |
licences[].ip |
the server's public IPv4 address |
licences[].status |
the licence's state, for example active |
licences[].products |
its products, each {"product", "name", "plan", "plan_name"}, and for HF Cloud / HF Deploy "limit" (0 = unlimited) and "unit" |
licences[].install |
the install command for that server, with the key in it |
For large fleets, use the paged fleet call instead.
Issue a licence
Issue a licence for a new server's public IPv4 address, then run the install command on that server.
curl -s https://api.hostfog.com/v1/licence/partner/issue \
-H "Authorization: Bearer hfp_…" \
-H "Idempotency-Key: order-58213" \
-d '{"email": "customer@example.com", "ip": "203.0.113.10"}'
{"key": "HFL-…", "install": "cd /home && curl -o latest -L … && bash latest --licence HFL-…"}
Issuing is refused with 409 when the pool is full (or your cap is reached), when that IP address already has a
licence, when the address is not a public IPv4 address, or when a plan in products does not exist.
Products
One licence key can carry HF Panel, HF Cloud and HF Deploy. Send products to choose them; without it
the licence has every HF Panel feature (HF Cloud and HF Deploy only as their own products):
{"email": "customer@example.com", "ip": "203.0.113.10", "products": {"panel": "none", "cloud": "", "deploy": "deploy-pro"}}
| Key | Value |
|---|---|
panel |
an HF Panel plan key (starter, pro, business …), or none (left out = none) |
cloud |
an HF Cloud plan key (the plans the publisher adds), or "" for none |
deploy |
an HF Deploy plan key (deploy-starter …), or "" for none |
At least one product is needed. A licence without HF Panel gets a small Server Admin and installs only what its
products need. To change the products of a licence later (an upgrade or a downgrade), send the same object to
POST /v1/licence/partner/products with its key; the server picks the change up at its next licence check, and
your webhook hears licence.changed. The plan keys are those of the public catalogue
(https://api.hostfog.com/v1/licence/catalog: plans and products).
Tip: to install without any questions, add
--hostnameand
Move a licence
When a server gets a new IP address, move its licence. The key stays the same.
curl -s https://api.hostfog.com/v1/licence/partner/move \
-H "Authorization: Bearer hfp_…" \
-d '{"key": "HFL-…", "ip": "203.0.113.20"}'
Then run hf-agent licence sync on the server, or wait for its next nightly check.
Remove a licence
When you delete a server, remove its licence. Its place in the pool is free at once.
curl -s https://api.hostfog.com/v1/licence/partner/remove \
-H "Authorization: Bearer hfp_…" \
-d '{"key": "HFL-…"}'
Warning: the panel on that server locks at its next licence check. Websites, email and DNS keep running, but nobody can sign in to the panel. Remove a licence only when the server is gone or the customer has left. A licence removed by mistake can be restored by HostFog Ltd within 30 days.
Bulk calls
issue-bulk and remove-bulk handle up to 500 licences in one call, for example when you set up a new data
centre or retire a range of servers. More than 500 answers 422: split the list.
curl -s https://api.hostfog.com/v1/licence/partner/issue-bulk \
-H "Authorization: Bearer hfp_…" \
-H "Idempotency-Key: rack-12-2026-11" \
-d '{"atomic": false, "servers": [
{"email": "one@example.com", "ip": "203.0.113.31"},
{"email": "two@example.com", "ip": "203.0.113.31"}]}'
{"issued": 1, "failed": 1, "results": [
{"index": 0, "email": "one@example.com", "ip": "203.0.113.31", "key": "HFL-…", "install": "…"},
{"index": 1, "email": "two@example.com", "ip": "203.0.113.31", "error": "That server already has a licence."}]}
The list may also be called licences instead of servers. The answer is 200 even when some items were refused:
check failed and each result's error. index is the item's place in your list, from 0.
atomic |
What happens when one item is refused |
|---|---|
false (the default) |
every other item still goes through. The answer lists each item with its own result: the new key and install command, or the reason it was refused. |
true |
nothing changes at all: the answer is 409 with {"error", "failed", "results"} saying which items were refused and why. Fix them and send the whole list again. For issue-bulk, the pool is checked first: when it has no room for every server, the answer is 409 at once. |
remove-bulk takes {"keys": ["HFL-…", "HFL-…"], "atomic": …} and answers one result per key:
{"index", "key", "removed": true} or {"index", "key", "error"}. A key that is not yours, or the same key twice, is
refused like any other item.
Tip: use
"atomic": truewhen a half-done batch would be harder to clean up than a refused one, for example when the pool may not have room for all of them.
The fleet
GET /v1/licence/partner/fleet lists your servers one page at a time, with what each licensed server last reported
in its daily licence check: its HF Panel version, when it was last seen and how many hosting accounts it runs.
| Parameter | What it does |
|---|---|
?page |
the page number, from 1 |
?per |
servers per page, 1 to 500 (default 100) |
?status |
only licences in this state: active, new, unseen or suspended |
?version |
only servers that run this HF Panel version, for example 1.0.0 |
A wrong parameter answers 422. Each server has one status:
| Status | Means |
|---|---|
active |
the licence works and the server checked in within the last 7 days |
new |
the licence works but the server has not checked in yet (issued less than 7 days ago) |
unseen |
the licence works but the server has not checked in for 7 days or more |
suspended |
the licence does not work: it was revoked, or your bulk licences ended (licence_status says which) |
Each item of licences has key, email, ip, hostname, status, licence_status, last_check_at (UTC, or
null), last_check_ip, version, accounts (null until the server first checks in), plan, tier,
features (null means every feature) and created_at. counts gives the number of servers in each status over all
your licences, before the filters, and pages the number of pages.
curl -s "https://api.hostfog.com/v1/licence/partner/fleet?status=active&version=1.0.0&page=1&per=100" \
-H "Authorization: Bearer hfp_…"
Use ?version to find the servers still on an older release during a rollout. Read pages
from 1 to pages.
Safe retries: Idempotency-Key
Networks fail. If a call to issue licences times out, you cannot tell whether the licence was made. Send an
Idempotency-Key header with every POST, for example your order or server number:
- the same key with the same body answers the first call's answer again, with the header
Idempotent-Replayed: true, and changes nothing a second time (a refusal is answered again too); - while the first call is still running, a repeat answers 409: wait a moment and try again;
- the same key with a different body is refused with 422;
- a key is up to 255 printable characters without spaces (else 400), and is your own: other partners' keys never clash with it.
Keys are remembered for 24 hours. Use a new key for each new action. An answer of 429 or 500 and more is not remembered, so the same key can be tried again.
Errors and limits
Every refusal answers JSON: {"error": "…"}, a sentence you can log or show to staff.
| Status | Means |
|---|---|
| 400 | the Idempotency-Key header is too long or has spaces |
| 401 | no API key, or not a valid one |
| 403 | your bulk licences are not active (payment) |
| 404 | no such licence among yours |
| 405 | the wrong method: GET for licences and fleet, POST for the others |
| 409 | refused: the pool is full or the cap is reached, the server already has a licence, the address is not a public IPv4 address, an atomic bulk call had a refused item, or an identical call is still running |
| 422 | the request itself is wrong, for example more than 500 items in a bulk call or a wrong fleet parameter; or an Idempotency-Key used for another call |
| 429 | too many calls: try again later |
The API allows about 600 calls an hour from one address. Use the bulk calls and the fleet for large jobs, and wait before retrying after a 429.
The same calls also answer at /licence/api/partner/… on the licence server's own address, for systems that cannot
reach api.hostfog.com.