Skip to content
Hostio

API reference

The Hostio API

Read your servers and sites, create sites and request certificates from your own tools. JSON over HTTPS, one key per integration.

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_url may 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 in message.
  • 422: the request body or a query parameter is invalid.
  • 429: rate limit; wait for Retry-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 (after Retry-After when 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.com covers a.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 and dns01 are 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): http01 or dns01.
  • 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.

All guides

Start with a free health check.

Also from Talivio: TCSR A11yProof PrivaScan