Introduction
F4 Panel provides a REST API to manage hosting from scripts or external systems — create sites, databases, members, login links and more. All endpoints use HTTPS and return JSON.
Base URL
https://host-th-sv1.f4host.com/api/v1
Sandbox Base URL test
https://host-th-sv1.f4host.com/api/sandbox/v1
Use the sandbox base URL to test your integration safely — same tokens, scopes and IP rules apply, but requests make no real changes.
Authentication
Create a token on the API page, then send it with every request in the header:
Authorization: Bearer <your-token>
curl -H "Authorization: Bearer <token>" \ https://host-th-sv1.f4host.com/api/v1/ping
Sandbox test mode
Test the API without side effects. The sandbox accepts the same tokens, scopes and IP restrictions and validates your request, then returns a simulated response — nothing is created, changed or deleted. Swap the base URL to go live.
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{"domain":"test.com","type":"php"}' https://host-th-sv1.f4host.com/api/sandbox/v1/sites
# -> {"ok":true,"sandbox":true,"method":"POST","endpoint":"/sites", ... }
Scopes
A token reaches exactly what it was granted. One scope is one action on one group — sites:read can read a site and cannot create or delete one. Calling outside them returns HTTP 403, and nothing is changed.
* | Full access, including endpoints added later |
<group>:* | Every scope in that group, e.g. sites:* — including ones added to it later |
🌐 Websites
sites:*6 scopes
sites:read |
List sites, read details and bandwidth GET /sites GET /sites/:domain GET /sites/:domain/bandwidth |
sites:write |
Create new websites POST /sites |
sites:delete destructive |
Delete websites (including ?purge=1, which drops files and databases) DELETE /sites/:domain |
sites:php |
Switch a site PHP version POST /sites/:domain/php |
sites:wordpress |
Install WordPress on a site POST /sites/:domain/wordpress |
sites:suspend |
Suspend / unsuspend a site POST /sites/:domain/suspend · /unsuspend |
📁 Files
files:*2 scopes
files:write |
Upload files into a site and extract .zip archives POST /sites/:domain/files |
files:link |
Mint a one-time link into the file manager POST /sites/:domain/filemanager |
🗄 Databases
db:*4 scopes
db:read |
List a site databases (never passwords) GET /sites/:domain/db |
db:write |
Create a database for a site POST /sites/:domain/db |
db:import destructive |
Import a dump over a database POST /sites/:domain/db/import |
db:link |
Mint a one-time link into phpMyAdmin POST /sites/:domain/phpmyadmin |
👥 Members
members:*7 scopes
members:read |
List members, read their details and usage GET /members GET /members/:id GET /members/:id/usage |
members:write |
Create member accounts POST /members |
members:password destructive |
Set a member panel password POST /members/:id/password |
members:plan |
Move a member to another plan POST /members/:id/plan |
members:suspend |
Suspend / unsuspend a member account POST /members/:id/suspend · /unsuspend |
members:ssh destructive |
Grant or revoke a member shell access POST /members/:id/ssh |
members:delete destructive |
Delete a member (including ?purge=1, which deletes all their sites) DELETE /members/:id |
🔑 Login links & provisioning
access:*2 scopes
sso destructive |
Mint a one-time login link as a user (120s TTL) POST /sso |
provision |
Provision in one call: member + site + WordPress + DNS zone + login link POST /provision |
📦 Plans
plans:*3 scopes
plans:read |
List the plans you may sell GET /plans |
plans:write |
Create and edit plans POST /plans POST /plans/:id |
plans:delete destructive |
Delete a plan DELETE /plans/:id |
📊 Server & cron
monitor:*2 scopes
server:read |
Read server statistics GET /server/stats |
cron:read |
Read the cron job list GET /cron |
🕰 The original scope names (still valid)
Tokens issued before granular scopes existed carry the five original names. Every one of them still behaves exactly as it did: nothing needs reissuing and no calling code needs changing. Each name covers what it always covered.
sites | Everything under Websites, Files and Databases
= sites:read · sites:write · sites:delete · sites:php · sites:wordpress · sites:suspend · files:write · files:link · db:read · db:write · db:import · db:link |
members | Everything under Members, plus login links and provisioning
= members:read · members:write · members:password · members:plan · members:suspend · members:ssh · members:delete · sso · provision |
plans | Everything under Plans
= plans:read · plans:write · plans:delete |
server | Server statistics
= server:read |
cron | The cron job list
= cron:read |
Note: POST /provision used to be reachable with any valid token, because no rule covered it. Existing tokens keep that reach; tokens created or re-saved through the granular picker must be granted provision explicitly.
IP & Security
Restrict each token to specific IPs or CIDR ranges — requests from other IPs get HTTP 403. Keep tokens secret like a password and always use HTTPS.
Response format
Every response is JSON:
{"ok": true, ...data... }
{"ok": false, "error": "message" }
Missing/invalid token → 401 · out of scope/IP → 403 · not found → 404
Sites sites:* · files:*
/sitessites:readList your sites/sites/:domainsites:readSite detail + DB info/sitessites:writeCreate site {domain,type,db,aliases}/sites/:domainsites:deleteDelete site (add ?purge=1 to also drop files, system user and databases)/sites/:domain/phpsites:phpSwitch PHP {version}/sites/:domain/wordpresssites:wordpressInstall WordPress/sites/:domain/bandwidthsites:readBandwidth this month/sites/:domain/suspendsites:suspendSuspend / unsuspend/sites/:domain/filemanagerfiles:linkOne-time link straight into the file manager {path?}/sites/:domain/phpmyadmindb:linkOne-time link straight into phpMyAdmin {db?}/sites/:domain/filesfiles:writeUpload a file (multipart) — a .zip is extracted in place {file,path?,extract?,keep_zip?}Example: upload a zip and extract it into public
curl -X POST -H "Authorization: Bearer <token>" \ -F "[email protected]" -F "path=public" \ https://host-th-sv1.f4host.com/api/v1/sites/example.com/files # → {"ok":true,"path":"public","file":"site.zip","bytes":8340213,"human":"8.0 MB", # "extracted":true,"zip_kept":false}
| Field | Default | Meaning |
|---|---|---|
file | — | The uploaded file (multipart/form-data), at most 256 MB |
path | public | Destination folder relative to the site root, e.g. public/app |
extract | 1 | Extract a .zip automatically — send 0 to just store it |
keep_zip | 0 | Keep the .zip after extracting (by default it is deleted, so the archive is not left published) |
If the upload succeeds but extraction fails you get HTTP 207 with a warning — the file is already on the server, do not re-upload. An oversized file returns HTTP 413.
Example: create a site
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"domain":"example.com","type":"php","db":"mariadb"}' \
https://host-th-sv1.f4host.com/api/v1/sites
Databases & Cron db:* · cron:read
/sites/:domain/dbdb:writeCreate database {engine}/sites/:domain/dbdb:readEvery database this site has (name/engine/host/port — no passwords)/sites/:domain/db/importdb:importImport a dump (multipart) .sql / .sql.gz / .zip {file,database?,mode?}Example: move a whole site — files and database
curl -X POST -H "Authorization: Bearer <token>" \ -F "[email protected]" https://host-th-sv1.f4host.com/api/v1/sites/example.com/files curl -X POST -H "Authorization: Bearer <token>" \ -F "[email protected]" -F "mode=replace" \ https://host-th-sv1.f4host.com/api/v1/sites/example.com/db/import # → {"ok":true,"database":"example_com","engine":"mariadb","mode":"replace", # "tables":42,"snapshot":"/var/backups/hosting/.pre-import/example.com/..."}
| Field | Default | Meaning |
|---|---|---|
file | — | The dump: .sql, .sql.gz, or a .zip holding exactly one .sql — at most 256 MB |
database | the site main database | Must be a database this site owns; any other name is refused, not created |
mode | merge | merge imports over what is there (tables absent from the dump survive) · replace empties the database first |
The current contents are always dumped before an import and the path comes back as snapshot (the ten most recent are kept per site). If that snapshot cannot be taken, the import is refused.
/croncron:readList cron jobsMembers & SSO members:* · sso · provision
/membersmembers:readList members/members/:idmembers:readOne member (plan, quota, owned domains, suspension state)/membersmembers:writeCreate member (admin/reseller) {username,password,planId} — refused with 403 for a Private Hosting account/members/:id/passwordmembers:passwordChange a customer's panel password {password}/members/:id/planmembers:planMove to another plan {planId}/members/:id/sshmembers:sshGrant/revoke a member's shell access {ssh:true|false} — administrator only; revoking removes their keys and cuts live sessions/members/:id/suspendmembers:suspendSuspend the account — suspends every site the customer owns/members/:id/unsuspendmembers:suspendUnsuspend the account/members/:idmembers:deleteDelete account / cancel service — ?purge=1 also deletes all their sites/ssossoThe “Log in to Panel” button — one-time login link, 120s TTL {user}/provisionprovisionProvision in one call: member + site + optional WordPress + optional DNS zone + login linkReseller members:* · plans:read
Every members endpoint above already works for a reseller, scoped to the customers they created (parent = that reseller). The three below exist to build a reseller portal.
/meany tokenWho you are, what is left of your allowance, and your customers in aggregate (any valid token)/members/:id/usagemembers:readPer-customer usage: sites, disk and bandwidth against the plan limits/plansplans:readThe plans you may sell — a reseller sees only what the administrator allowed, not the whole catalogueA planId you are not allowed to sell returns 403 you may not assign that plan — on create, on plan change and on provision alike.
Example: sell a package and hand over a login link
curl -X POST -H "Authorization: Bearer <reseller-token>" \
-H "Content-Type: application/json" \
-d '{"username":"somchai","password":"a-long-password","domain":"somchai.com","planId":"pl_...","wordpress":true}' \
https://host-th-sv1.f4host.com/api/v1/provision
# → {"ok":true,"memberCreated":true,"member":{...},"site":{...},
# "wordpress":{...},"dns":null,"loginUrl":"https://.../sso/<once>","expires_in":120}
Example: a “Files” / “Database” button in your billing site
curl -X POST -H "Authorization: Bearer <token>" https://host-th-sv1.f4host.com/api/v1/sites/customer.com/filemanager
# → {"ok":true,"url":"https://.../sso/<once>","dest":"/site/customer.com/files?p=public"}
# open the returned url → the customer lands in the file manager, auto-logged-in, single-use
Example: auto-provision when an order is paid
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{
"username":"customer1","password":"S3cret-Passw0rd","planId":"pl_xxx",
"domain":"customer-site.com","wordpress":{"title":"My shop","user":"admin","email":"[email protected]"},
"dns":true }' https://host-th-sv1.f4host.com/api/v1/provision
# → {"ok":true,"member":{...},"site":{...},"wordpress":{"adminUrl":"..."},"loginUrl":"https://.../sso/<once>"}
Example: one customer lifecycle
// 1) provision
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{"username":"customer1","password":"S3cret-Passw0rd","planId":"pl_xxx"}' https://host-th-sv1.f4host.com/api/v1/members
// 2) log in button
curl -X POST ... -d '{"user":"customer1"}' https://host-th-sv1.f4host.com/api/v1/sso
// 3) reset password
curl -X POST ... -d '{"password":"N3w-Passw0rd"}' https://host-th-sv1.f4host.com/api/v1/members/<id>/password
// 4) unpaid → suspend
curl -X POST ... https://host-th-sv1.f4host.com/api/v1/members/<id>/suspend
// 5) cancel (delete everything)
curl -X DELETE ... "https://host-th-sv1.f4host.com/api/v1/members/<id>?purge=1"
Example: auto-login link (SSO)
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" -d '{"user":"customer1"}' \
https://host-th-sv1.f4host.com/api/v1/sso
# → {"ok":true,"url":"https://.../sso/<once>","expires_in":120}
Plans & Server plans:* · server:read
/plansplans:readList plans/plansplans:writeCreate plan {name,maxSites,diskMB,bandwidthMB,maxAccounts,maxDatabases,maxSubdomains,maxAliases,maxFtpAccounts,cpuPercent,ramMB,phpWorkers,phpMemoryMB,maxExecSec,abuseGraceMin,abuseCooldownMin,whiteLabel}/plans/:idplans:writeEdit a plan you own — send only the fields you want changed/plans/:idplans:deleteDelete a plan you own — accounts on it are left with no plan rather than inheriting somebody else quotasPlans have an owner: one a reseller creates belongs to that reseller, one an administrator creates is a system plan. A reseller sees and edits only their own, and maxAccounts is administrator-only — a reseller cannot mint another reseller.
Every quota is an integer and 0 means unlimited — except maxSites, where a negative value means unlimited.
| Field | Meaning |
|---|---|
maxSites | Total sites on the account, subdomains included |
diskMB | Total disk space (MB) |
bandwidthMB | Monthly traffic (MB) |
maxAccounts | Sub-accounts a reseller may create |
maxDatabases | Databases across every site on the account |
maxSubdomains | Subdomains — sites under a domain the account already owns |
maxAliases | Parked domains across all sites; the automatic www. is not counted |
maxFtpAccounts | FTP accounts across every site on the account |
cpuPercent | CPU per website as a percentage of ONE core (100 = a full core) · 0 = unlimited |
ramMB | RAM ceiling per website in MB · 0 = unlimited |
phpWorkers | Concurrent PHP requests per website (pm.max_children) · 0 = derived from ramMB |
phpMemoryMB | PHP memory_limit for a single request · 0 = derived automatically |
maxExecSec | PHP max_execution_time · 0 = PHP's own default |
abuseGraceMin | Suspend the site after this many consecutive minutes pinned at its ceiling · 0 = throttle only, never suspend |
abuseCooldownMin | Auto-restore this many minutes after suspension · 0 = an administrator has to lift it |
whiteLabel | Lets the plan brand the panel as its own (settable by the server administrator only) |
/server/statsserver:readServer stats/pingany tokenTest token + who am IF4 Panel · hosting control panel · developed by F4 Code
API