API REST conventions
How every token API resource is named, listed, paged, filtered and refused, and how errors look.
The newer token API resources follow one set of rules, and the older ones follow them where that does not change what they answer today. Everything is JSON in and out.
A=https://api.yourdomain.com/v1
H="Authorization: Bearer hf_xxx"
Names and methods
| Call | Meaning |
|---|---|
GET /v1/…/things |
list |
GET /v1/…/things/{id} |
one |
POST /v1/…/things |
create. Answers 201 with a Location header that is the new item's address |
PATCH /v1/…/things/{id} |
change the fields you send; the others stay |
PUT |
replace a setting that has one value (PHP settings of a website, an autoresponder, the quiet hours) |
DELETE /v1/…/things/{id} |
remove |
POST /v1/…/things/{id}/<verb> |
do something: start, restart, close, mark-paid, test … |
Resources are plural nouns (/accounts/{username}/mailboxes, /ftp-accounts, /cron-jobs …; see
single items).
Lists, paging and filters
A list answer has data (the page) and meta:
{
"ok": true,
"data": [ … ],
"meta": { "total": 120, "page": 2, "per_page": 50, "total_pages": 3 }
}
The X-Total-Count header carries total too.
| Query | Meaning |
|---|---|
page |
which page, from 1 |
per_page |
items per page, 1 to 200 |
q |
only items with these words in any of their values |
status |
only items with this status. A list that has its own status values (/tickets, /orders, /invoices) says which in its description |
The newer resources page by default (50 per page). The older lists answer everything until you send page or
per_page, so scripts that read them keep working. A bad value (page=x, per_page=0) answers 422.
Errors
| Status | When |
|---|---|
| 400 | the body is not JSON |
| 401 | no token, a wrong token, or a token from a refused address |
| 402 | the server's licence lacks the module (Email, DNS, Billing …) |
| 403 | the token may not do this: read-only token, wrong kind of login, a team role without it |
| 404 | no such account, item or endpoint (an account that is not yours is "No such account.") |
| 409 | it exists already, or is in use |
| 422 | a field is missing or wrong; the answer names the field |
| 429 | too many requests (Retry-After, X-RateLimit-* headers), or the sign-in lock-out |
The error body stays what it always was, so billing modules and scripts that read it keep working:
{ "ok": false, "error": "target is required." }
Ask for the standard error object with the header X-HF-Errors: object (or ?errors=object):
{
"ok": false,
"error": {
"code": "validation_failed",
"message": "target is required.",
"field": "target"
}
}
The codes are bad_request, unauthorized, licence_required, forbidden, not_found, conflict,
validation_failed, rate_limited, upstream_error and server_error; fields lists the messages of every field when a form check
found several. An administrator can make the object the default for every caller with "api_error_format": "object"
in the panel's configuration; do that only when nothing on the server still reads the text form (billing modules,
older scripts).
Idempotency and limits
Send an Idempotency-Key header with a POST that creates something. The same key with the same body within 24
hours answers the first answer again (with Idempotent-Replayed: true) and does nothing twice; the same key with
another body answers 422. Every answer has X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.
Who may do what
The rules of the panel apply to every resource. A customer's token reaches its own data. A reseller's token reaches
its customers'. Administrator-only resources (/webhooks, /staff, /firewall-rules, /server/updates …) answer
403 to everyone else. A Read only token, or a read-only staff login, can only read. A
team-role token reaches only what its role allows.
The description at GET /v1/openapi.json shows the role each operation needs (x-hf-who, x-hf-write).