PanelDOCSv1.0.0

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

bash
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.

bash
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"}'
json
{"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):

json
{"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 --hostname and --email to the install command. See Customise the installation.

Move a licence

When a server gets a new IP address, move its licence. The key stays the same.

bash
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.

bash
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.

bash
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"}]}'
json
{"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": true when 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.

bash
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.