Projects and roles¶
A project holds servers, SSH keys, firewall groups, snapshots, ISOs, actions and orders, and the people who manage them. Each person has one role in each project they belong to. Paths address a project by its slug: /projects/acme/servers/.
Endpoints¶
| Endpoint | What it does |
|---|---|
GET / |
List projects |
POST / |
Create a project |
GET / |
Get a project |
PATCH / |
Rename a project |
DELETE / |
Delete a project |
GET / |
List members |
PATCH / |
Change a member's role |
DELETE / |
Remove a member |
GET / |
List invitations |
POST / |
Invite someone |
DELETE / |
Withdraw an invitation |
POST / |
Resend an invitation |
Roles¶
| Member | Admin | Owner | |
|---|---|---|---|
| Read everything, and poll actions and orders | |||
| Power, reboot and reinstall servers, and take and restore snapshots | |||
| Manage hostnames, SSH keys, firewalls, ISOs and reverse DNS | |||
| Order servers, upgrade them, buy bandwidth and cancel servers | |||
| Rename the project, and manage its members and invitations | |||
| Delete the project |
A request your role doesn't allow answers 403 insufficient_role. A project you are not a member of answers 404 not_found, the same as one that doesn't exist, so a 404 never tells anyone a project exists.
A read-only key can only read, whatever your role.
Transferring a server to another account is done in the console. The API has no endpoint for it.
Project slugs¶
The slug is the project's id in every path. You choose it when you create the project, and it can't change afterwards.
- 3 to 40 characters: lowercase letters, digits and hyphens.
- It starts with a letter and doesn't end with a hyphen:
^<span class="tooltip hint--top" aria-label="1,38">a-z][a-z0-9-</span>[a-z0-9]$. - These words are reserved:
api,admin,accounts,new,create,default,me,projects,servers,billing,support,static,media,www,hosthatch. - A slug is never used twice, not even after its project is deleted.
Leave the slug out and one is made from the name, with four random digits: Acme becomes acme-4821.
Projects created before these rules keep their slugs, which may have capital letters or underscores, and they work in every path.
Project readiness¶
A new project takes a moment to set up. Until it is ready, its ready field is false and every endpoint for its servers and other resources answers 403 project_not_ready. If the project's owner has no billing account yet, those endpoints answer 403 billing_account_required instead.
The project object¶
{
"id": 7,
"name": "Acme",
"slug": "acme",
"ready": true,
"role": "owner",
"created_at": "2026-10-19T10:00:00Z"
}
| Field | Type | Description |
|---|---|---|
id |
integer | The project's id. Paths use the slug instead. |
name |
string | A label you can change. It never appears in paths. |
slug |
string | The project's id in paths. It never changes. |
ready |
boolean | false while the project is being set up. |
role |
string | Your role in it: owner, admin or member. |
created_at |
string | When it was created. |
Projects¶
List projects¶
GET /
The projects you are a member of. A user without a project gets an empty list.
Create a project¶
POST /
Create a project. You become its owner.
curl -X POST https://console.hosthatch.com/api/v1/projects/ \
-H "Authorization: Bearer $HOSTHATCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme", "slug": "acme-prod"}'
| Field | Type | Description |
|---|---|---|
name |
string | Required. |
slug |
string | Optional. See Project slugs for the rules, and for what happens when you leave it out. |
You can have up to 20 projects. One more answers 409 limit_reached. A slug that breaks the rules answers 400 validation_error, and one another project has, or had, answers 409 name_taken.
Get a project¶
GET /
One project.
Rename a project¶
PATCH /
Change the project's name, the only thing about it that can change. Sending a different slug answers 400 validation_error.
{"name": "Acme production"}
Delete a project¶
DELETE /
Delete an empty project. It must have no servers, no SSH keys, no orders in progress and no running actions; otherwise the answer is 409 project_not_empty. While the deletion runs, new orders and actions in the project answer 409 project_deleting.
If you lose the answer, send the delete again: deleting a project that is already gone changes nothing.
Members¶
The member object:
{"user_id": 3, "name": "Jane Doe", "email": "[email protected]", "role": "admin"}
email is shown to admins and the owner only.
List members¶
GET /
The project's members and their roles.
Change a member's role¶
PATCH /
Change a member's role to admin or member. Nobody can change the owner's role, or make someone the owner.
{"role": "admin"}
Remove a member¶
DELETE /
Remove someone from the project. Admins and the owner can remove anyone except the owner, and any member can remove themselves. Removing a member, or making an admin a member, also withdraws every invitation they sent for the project that is still waiting.
Invitations¶
You invite someone by email. The email holds a link to accept, which takes them to the console. An invitation can be accepted only while the person who sent it is still an admin or the owner of the project.
The invitation object:
{
"id": "0b7e6a2c-5d1f-4c3a-9e8b-2f6d4a1c7e90",
"email": "[email protected]",
"role": "member",
"invited_by": "Jane Doe",
"created_at": "2026-10-19T10:00:00Z"
}
List invitations¶
GET /
The invitations that are waiting to be accepted.
Invite someone¶
POST /
Invite someone, as member or admin.
{"email": "[email protected]", "role": "member"}
An email that already has an invitation waiting answers 409 invitation_pending: resend that one instead. Inviting needs a billing account of your own; without one the answer is 403 billing_account_required.
Withdraw an invitation¶
DELETE /
Withdraw an invitation. Its link stops working.
Resend an invitation¶
POST /
Send the invitation's email again. The answer is the invitation.
Errors¶
| Status | Code | When |
|---|---|---|
| 400 | validation_error |
A field is invalid, such as a slug that breaks the rules or a changed slug. |
| 403 | insufficient_role |
Your role doesn't allow it. |
| 403 | billing_account_required |
Inviting without a billing account. |
| 403 | project_not_ready |
The project is still being set up. |
| 404 | not_found |
No such project, member or invitation, or you are not a member. |
| 409 | limit_reached |
You already have 20 projects. |
| 409 | name_taken |
The slug is taken, or was used before. |
| 409 | project_not_empty |
Deleting a project that still has resources, orders or actions. |
| 409 | project_deleting |
Ordering or starting an action while the project is being deleted. |
| 409 | invitation_pending |
The email address already has an invitation waiting. |