curl https://hostio.talivio.com/api/v1/servers/12 \
-H "Authorization: Bearer $HOSTIO_TOKEN" \
-H "Accept: application/json"
API keys
The API comes with the Pro and Agency plans. A team owner or admin creates keys under the user menu → API keys. The key belongs to the team, not to the person who made it.
- Abilities: tick only what the integration needs. A call without the ability is refused with 403, before Hostio looks up the id.
- Sites: limit the key to some sites, or leave it empty for every site of the team, including future ones.
- Addresses: an optional list of IPs or CIDR ranges the key may be used from.
- Webhook hosts: the hosts a certificate
webhook_urlmay point at. - Expiry: optional. The key works until the end of that day.
The key is shown once. Hostio keeps only its hash. Revoking it takes effect at once.
Authorization: Bearer <your key>
Abilities: servers:read, sites:read, certificates:read, databases:read, sites:create, certificates:write, domains:write.
Rate limits
- 120 reads (GET) a minute per key.
- 30 writes (POST, DELETE) a minute per key, counted apart from reads.
- 60 requests a minute per address for calls without a valid key.
Over the limit the answer is 429 with Retry-After (seconds). Every answer carries X-RateLimit-Limit and X-RateLimit-Remaining. A certificate request is also limited to 10 new orders per site per hour.
Errors
Every error is JSON with a message. A 422 adds errors, one list per field. A refusal's message starts with refused: and says why.
{"message": "This token lacks the \"sites:create\" ability."}
401: no key, a wrong key, or an expired or revoked one.402: the team's plan has no API access.403: the key lacks the ability, or is used from an address outside its list.404: no such record for this team. Another team's id is a 404 too, never a 403.409: refused, with the reason inmessage.422: the request body or a query parameter is invalid.429: rate limit; wait forRetry-After.501: the endpoint exists but this action is not built; the message says why.502,503: the server or the panel that still manages the site could not complete the change now; try again later (afterRetry-Afterwhen it is sent).
Lists
List endpoints answer {"data": [...], "meta": {...}}. Ask for a page with page and per_page (default 50, at most 100).
Writes that take longer
A write runs on the server while the request waits, up to 25 seconds. If it finished, the answer is 201 (or 204 for a removal). If it is still running, the answer is 202 with the state so far; read the resource again until it changes (a new site goes from creating to active or failed).
Writes are safe to repeat: sending the same request again answers 200 with the existing result instead of doing the work twice.
Endpoints
Paths are relative to https://hostio.talivio.com/api/v1. {server} and {site} are Hostio's own ids, as the panel and the list endpoints show them.
GET /api/v1/servers/{server}
One server. Ability: servers:read.
{"data": {"id": 12, "name": "web-1", "ip": "203.0.113.10", "ipv6": null,
"hostname": "web-1", "status": "ready"}}
status is pending, provisioning, ready or unreachable.
GET /api/v1/servers/{server}/sites
The sites on a server, ordered by id. Ability: sites:read.
page,per_page: see Lists.
{"data": [{"id": 152, "server_id": 12, "domain": "shop.example.com", "...": "…"}],
"meta": {"current_page": 1, "last_page": 1, "per_page": 50, "total": 1}}
A key limited to some sites sees only those.
GET /api/v1/sites/{site}
One site. Ability: sites:read.
{"data": {"id": 152, "server_id": 12, "domain": "shop.example.com",
"aliases": ["www.shop.example.com"], "status": "active",
"project_type": "laravel", "php_version": "8.4", "ploi_php_version": null,
"system_user": "shop", "web_directory": "/public", "has_repository": true,
"last_deploy_at": "2026-10-30T09:12:44Z", "disk_usage_mb": null,
"created_at": "2026-10-20T11:02:10Z", "provider_created_at": null,
"ploi_site_id": null, "owner": "box"}}
status is active or broken for a running site, creating or failed while Hostio creates it. disk_usage_mb is not measured yet and is always null.
GET /api/v1/sites/{site}/certificates
The certificates of a site, including its tenants'. Ability: certificates:read.
domain(optional): only certificates that cover this name;*.example.comcoversa.example.com.page,per_page: see Lists.
{"data": [{"id": 88, "site_id": 152, "tenant": null,
"domains": ["shop.example.com", "www.shop.example.com"],
"status": "issued", "installed": true, "type": "letsencrypt",
"method": "http01", "expires_at": "2027-01-28T09:00:00Z",
"created_at": "2026-10-30T09:00:00Z"}],
"meta": {"current_page": 1, "last_page": 1, "per_page": 50, "total": 1}}
status is requested, issued or failed. A request still requested after 30 minutes becomes failed.
GET /api/v1/servers/{server}/databases
The databases on a server, and the sites that use them. Ability: databases:read.
page,per_page: see Lists.
{"data": [{"id": 40, "server_id": 12, "name": "shop", "type": "mysql", "system": false,
"sites": [{"id": 152, "domain": "shop.example.com", "via": "env"}]}],
"meta": {"current_page": 1, "last_page": 1, "per_page": 50, "total": 1}}
via says how Hostio linked the site: its .env, its wp-config.php, or the panel that created it. system marks the server's own databases.
POST /api/v1/servers/{server}/sites
Create a site: system user, PHP-FPM pool and nginx vhost. Ability: sites:create.
domain(required): the site's domain.php_version(optional): an installed PHP version; the server's default when left out.system_user(optional): derived from the domain when left out.reuse_system_user: true to put the site under an existing user.web_directory(optional): default/public.project_type(optional): stored as given.
{"domain": "shop.example.com", "php_version": "8.4", "web_directory": "/public"}
201 {"data": {"id": 153, "domain": "shop.example.com", "status": "active", "...": "…"}}
202 {"data": {"id": 153, "domain": "shop.example.com", "status": "creating", "...": "…"}}
On 202, read GET /api/v1/sites/{id} until status is active or failed. A key limited to some sites cannot create sites.
POST /api/v1/sites/{site}/certificates
Request a Let's Encrypt certificate for the site's own names. Ability: certificates:write.
domains(required): 1 to 100 names, each the site's domain or one of its aliases.method(required):http01. Wildcards anddns01are refused.webhook_url(optional): https on port 443, on one of the key's webhook hosts.
{"domains": ["shop.example.com", "www.shop.example.com"], "method": "http01",
"webhook_url": "https://hooks.example.com/hostio"}
201 {"data": {"id": 89, "site_id": 152, "status": "requested", "installed": false, "...": "…"}}
If a valid certificate already covers every name for 30 more days, the answer is 200 with that certificate. The certificate is installed once issued (installed: true); the webhook fires once at that moment.
Webhook body: event (certificate.installed), site_id, certificate_id, tenant, domains, installed_at. Headers: x-servio-event, x-servio-timestamp and x-servio-signature: sha256=<hex>, the HMAC-SHA256 of <timestamp>.<body> with your team's webhook signing key. Hostio support issues the key and shows it once.
POST /api/v1/sites/{site}/certificates/{certificate}/activate
Not built. Ability: certificates:write.
501 {"message": "Not implemented. There is nothing to activate: …"}
There is nothing to activate: a certificate requested through Hostio is installed as soon as it is issued. Custom certificates are not supported.
POST /api/v1/sites/{site}/tenants
Serve one more domain from a multi-tenant site. Ability: domains:write.
domain(required): the tenant's domain.
{"domain": "client-a.example.com"}
201 {"data": {"domain": "client-a.example.com", "state": "attached"}}
202 {"data": {"domain": "client-a.example.com", "state": "attaching"}}
200 if the domain is already attached. 409 if another site holds it.
DELETE /api/v1/sites/{site}/tenants/{domain}
Stop serving a tenant domain. Ability: domains:write.
204 (no body)
202 {"data": {"domain": "client-a.example.com", "state": "detaching"}}
404 if the site has no such tenant: treat it as already removed.
POST /api/v1/sites/{site}/tenants/{domain}/certificates
Request a certificate for a tenant. Ability: certificates:write.
domains(required): 1 to 10 names.method(required):http01ordns01.webhook_url(optional): as for a site certificate.
{"domains": ["client-a.example.com"], "method": "http01"}
201 {"data": {"id": 90, "tenant": "client-a.example.com", "status": "requested", "...": "…"}}
200 if an open request for the same tenant exists.
DELETE /api/v1/sites/{site}/aliases/{domain}
Remove an alias from a site. Ability: domains:write.
204 (no body)
202 {"data": {"domain": "old.example.com", "state": "removing"}}
The site's main domain is never removed (409). Certificates are not touched. There is no endpoint that adds an alias.