Cosmoner Docs
API Reference

Web Hosting

Create PHP hosting sites and manage their domains, redirects, files and MySQL databases.

Overview

A hosting site runs PHP on Apache in its own container and can answer on several hostnames at once. This page covers the API; the Web Hosting guide explains the folder layout, SFTP and how PHP runs.

All endpoints are project-scoped and sit under:

/v1/projects/:projectId/hosting/shared

Auth: Required. Read endpoints need the hosting:read scope and write endpoints hosting:write. A key scoped for dedicated servers does not reach hosting sites.


Sites

List sites

GET /v1/projects/:projectId/hosting/shared

Returns every site in the project that has not been deleted.

Get a site

GET /v1/projects/:projectId/hosting/shared/:tenantId

Returns the site with its limits, its address (url), the SFTP host and port, and ready, which says whether the site is serving right now. status says what was provisioned; ready is read live.

Add ?credentials=true to include sftpPassword. It is left out otherwise, so a page that shows connection details never has to fetch a password.

Get connection details

GET /v1/projects/:projectId/hosting/shared/:tenantId/access

Response:

{
  "success": true,
  "data": {
    "username": "h_mysite_x7k2mf",
    "host": "sftp.cosmoner.com",
    "sftp": { "port": 2022 },
    "ssh": { "port": 2022, "enabled": false }
  }
}

SFTP and SSH share one host and port, the same for every site, and the port is never 22. The username picks the site. Plain FTP and FTPS are not offered.

List tier prices

GET /v1/projects/:projectId/hosting/shared/prices

Returns the monthly list price for each tier, in minor units with an upper-case ISO currency code.

List available features

GET /v1/projects/:projectId/hosting/shared/features

Returns what this environment can add to a site: databases (boolean) and extraStorage (the add-on's price, or null). Check it before sending database or extraStorageGb on create.

Preview a charge

GET /v1/projects/:projectId/hosting/shared/preview?tier=<tier>&extraStorageGb=<gb>

Returns what creating a site with these options would charge today.

Create a site

POST /v1/projects/:projectId/hosting/shared

Body:

FieldTypeRequiredDescription
siteNamestringyes3–40 characters: a lowercase letter, then lowercase letters, numbers or underscores
tierstringnoSTARTER (default), GROWTH or SCALE
phpVersionstringno8.1, 8.2 or 8.3 (default)
extraStorageGbnumbernoDisk on top of the tier, in blocks of 10 GB
databaseobjectno{ "name": "<name>" } to create a MySQL database with the site

The prorated amount is charged to the project's payment method. There is no checkout redirect. A project without a payment method gets 402; see Billing → Deploy Payment Errors.

Response: { tenantId, status }. When database was sent, the response also carries database, or databaseError if the site was created and the database was not. The site is kept either way; create the database again from the databases endpoint.

Change tier

PATCH /v1/projects/:projectId/hosting/shared/:tenantId/tier

Body: { "tier": "STARTER" | "GROWTH" | "SCALE" }

New PHP workers, memory, domain and database limits apply straight away. Disk size does not change.

Delete a site

DELETE /v1/projects/:projectId/hosting/shared/:tenantId

Deletes the site, its files, its databases and its attached domains. This cannot be undone.


Domains

Every hostname a site answers on, other than its own address, is a domain you own or a subdomain of one. Each serves a folder, redirects, or is parked. The site's own cosmoner.com address takes no subdomains.

List domains

GET /v1/projects/:projectId/hosting/shared/:tenantId/domains

Response:

{
  "success": true,
  "data": [
    {
      "id": "cmf1…",
      "tenantId": "cmf0…",
      "hostname": "example.com",
      "kind": "SITE",
      "documentRoot": "/var/www/h_mysite_x7k2mf/example.com/public_html",
      "redirectTarget": null,
      "redirectStatus": null,
      "status": "ACTIVE",
      "routingStatus": "VERIFYING",
      "createdAt": "2026-09-19T10:00:00.000Z"
    }
  ]
}

routingStatus is VERIFYING until your DNS records are in place and the certificate is issued, then ACTIVE. It is refreshed each time the list is read. documentRoot is null unless kind is SITE.

Attach a domain

POST /v1/projects/:projectId/hosting/shared/:tenantId/domains

Body:

FieldTypeRequiredDescription
hostnamestringyesA domain you own, such as example.com or shop.example.com
kindstringnoSITE (default), REDIRECT or PARKED
pathstringnoSITE only. Folder to serve, relative to the site's home
redirectTargetstringREDIRECTFull http(s) address, without a query string. The requested path is appended
redirectStatusnumbernoREDIRECT only. 301 (default) or 302

A field that belongs to another kind is refused with 400, not ignored.

Without path, a site is served from <hostname>/public_html. A subdomain of a domain the site already has defaults to a folder inside that domain's own: shop.example.com to example.com/shop. The folder is created if it does not exist.

Response:

{
  "success": true,
  "data": {
    "domainId": "cmf1…",
    "hostname": "example.com",
    "instructions": {
      "recordType": "CNAME",
      "name": "example.com",
      "value": "…",
      "ownershipVerification": { "type": "txt", "name": "…", "value": "…" }
    }
  }
}

Add the records in instructions at your DNS provider.

Errors: 400 when the tier's domain limit is reached or the hostname is under cosmoner.com, 409 when the hostname is already attached to any site.

Change a domain

PATCH /v1/projects/:projectId/hosting/shared/:tenantId/domains/:domainId

Takes kind, path, redirectTarget and redirectStatus as above. The body is the domain's whole behaviour, not a partial update: { "kind": "SITE" } alone moves the domain to its default folder.

The hostname stays attached, so its DNS records and certificate are untouched.

Detach a domain

DELETE /v1/projects/:projectId/hosting/shared/:tenantId/domains/:domainId

The hostname stops pointing at the site. Any folder it served stays where it is, with its files.


Files

Paths are relative to the site's home folder, with no leading slash. An empty path is the home folder itself. .. is refused.

MethodPathDescription
GET/:tenantId/files?path=<folder>List a folder
GET/:tenantId/files/content?path=<file>Read a text file, up to 1 MB
GET/:tenantId/files/download?path=<file>Download a file, up to 64 MB
PUT/:tenantId/files/contentWrite a text file: { path, content }, up to 1 MB. Creates the file if needed
POST/:tenantId/files/upload?path=<folder>&name=<file>Upload one file. The body is the file, sent as application/octet-stream, up to 64 MB
POST/:tenantId/files/foldersCreate a folder: { path }
POST/:tenantId/files/renameRename or move: { from, to }
DELETE/:tenantId/files?path=<entry>Delete a file, or a folder and everything in it

Files run as the site's own user, so what the API creates is what an SFTP session sees and owns. Larger files go over SFTP.

A listing entry is { name, type, size, modifiedAt }, where type is file, directory, symlink or other.


Databases

MethodPathDescription
GET/:tenantId/databasesList the site's MySQL databases
GET/:tenantId/databases/:databaseIdGet one, including dbPassword
POST/:tenantId/databasesCreate one: { name }
DELETE/:tenantId/databases/:databaseIdDelete the database and its user

name is 1–16 lowercase letters and numbers, starting with a letter. The real database and user name are returned as dbName and dbUser, alongside host, port and maxConnections. The number of databases and the connections each one gets depend on the tier.

On this page