Developers

CoreHost API

Read and control your corehost.io apps from scripts, CI jobs and your own tools. Start, stop and rebuild apps, read their logs and settings, take, download and restore backups, and check your balance and invoices. Free on every account.

At a glance
Base URL
https://api.corehost.io/v1
Auth
Bearer token, scoped
Tokens
2 per account
Limits
120 reads and 30 writes a minute
Format
JSON, OpenAPI 3.1
Downloads
OpenAPI, Postman

Quick start

  1. Sign in and open the API tokens card on your profile page. Create a token with the read scope and copy it. It is shown once.

    Sign in to get your API key You will be asked to sign in or create a free account first.

  2. Keep it in an environment variable rather than in your code:

    export COREHOST_TOKEN="chk_..."
  3. Check the token works:

    curl -s https://api.corehost.io/v1/me \
      -H "Authorization: Bearer $COREHOST_TOKEN"
  4. List your apps. Each app's id is the one in your dashboard address, corehost.io/control/app/{id}:

    curl -s https://api.corehost.io/v1/apps \
      -H "Authorization: Bearer $COREHOST_TOKEN"
  5. Restart one (needs a token with apps:write):

    curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/power \
      -H "Authorization: Bearer $COREHOST_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"action": "restart"}'

Authentication

Every call except GET /catalog and GET /regions needs an API token in the Authorization header:

Authorization: Bearer chk_...
  • Tokens start with chk_. Create, list and revoke them on your profile page, where you can also see when each was last used. We email you each time one is created.
  • An account can hold 2 active tokens. Revoke one to make room for another.
  • A token is shown once, when you create it. We store only a fingerprint, so a lost token can't be recovered: revoke it and make a new one.
  • Give each token a name and the scopes it needs, and choose an expiry of 30, 90 or 365 days, or none. Revoking takes effect within 60 seconds.
  • Treat a token like a password. Keep it in an environment variable or a secrets manager, never in a repository.
  • Tokens can't create other tokens and don't work on the dashboard's own endpoints.
  • The API never returns app passwords, generated app secrets, SFTP passwords or console credentials. Recent console output is shown with known secrets masked. Use the dashboard for credentials.

Scopes

Give each token only the scopes it needs. A call outside the token's scopes answers 403 with the code insufficient_scope.

ScopeAllows
readList and view apps with live resource usage, read app logs, list backups, read app settings
apps:writeStart, stop, restart and kill apps; rebuild an app; create, restore and download backups; change app settings
apps:deleteDelete backups; delete apps
billing:readRead your balance, billing ledger and invoices

GET /me works with any scope. Downloading a backup needs apps:write because a backup holds all of an app's data.

Rate limits

Limits are per account, across all of its tokens: 120 read requests (GET) and 30 write requests (POST, PATCH and DELETE) per minute. Over the limit you get 429 with a Retry-After header giving the seconds to wait. Every response carries an X-Request-Id header.

HTTP/1.1 429 Too Many Requests
Retry-After: 23

{"error": {"code": "rate_limited", "message": "Too many requests. Wait a moment and try again.", "details": {"retry_after": 23}}, "request_id": "..."}

Errors

Every error uses the same envelope. Branch on code, show message to people, and quote request_id if you contact support.

{
  "error": {
    "code": "insufficient_scope",
    "message": "This token does not have the apps:write scope.",
    "details": {"required_scope": "apps:write"}
  },
  "request_id": "5f0c1c9e-8d7a-4b1e-9c0d-2a3b4c5d6e7f"
}
StatusCodesMeaning
400invalid_requestinvalid_cursorconfirm_requiredbackup_incompleteThe request was malformed or needs confirmation. Deleting an app and restoring a backup both ask you to confirm.
401unauthorizedMissing, malformed, expired or revoked token.
403insufficient_scopeforbiddenThe token lacks the scope, or the action is not allowed.
404not_foundNo such app, backup or invoice on your account. Apps on other accounts also answer 404.
409app_not_readyapp_busybackup_limit_reachedbackup_lockedidempotency_in_progressThe app cannot do that right now, or an identical request is still running.
429rate_limitedToo many requests. Wait Retry-After seconds.
502upstream_unavailableThe app platform could not be reached. Safe to retry.
500internal_errorSomething went wrong on our side.

A missing or invalid token is refused before it reaches the API; that 401 also carries a top-level message.

Pagination

List endpoints take limit (1 to 100, default 20) and cursor. When there is more, the response has a next_cursor; pass it back as cursor. A null next_cursor means you have everything.

curl -s "https://api.corehost.io/v1/billing/ledger?limit=50&cursor=$NEXT" \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Idempotency

Add an Idempotency-Key header to a POST, PATCH or DELETE and it is safe to retry after a timeout. Use any unique string of 8 to 64 characters (letters, digits and _ . : -), such as a UUID.

  • A repeat with the same key, method and path within 24 hours returns the first answer with Idempotent-Replayed: true and does not run again.
  • A repeat while the first request is still running gets 409 idempotency_in_progress. Wait a moment and retry.
  • If the first request failed with an error, the key is released and you can retry with it.
  • Keys belong to your account. The same key on another account does nothing to yours.
curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/backups \
  -H "Authorization: Bearer $COREHOST_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"

Examples

The same calls in three languages. They read the token from COREHOST_TOKEN and the app id from APP_ID. The JavaScript runs on Node 18 or newer and the Python needs pip install requests.

List your apps

Needs the read scope. Follow next_cursor to get every page.

curl -s "https://api.corehost.io/v1/apps?limit=50" \
  -H "Authorization: Bearer $COREHOST_TOKEN"
const BASE = "https://api.corehost.io/v1";
const headers = { Authorization: `Bearer ${process.env.COREHOST_TOKEN}` };

async function api(path, options = {}) {
  const res = await fetch(BASE + path, { ...options, headers: { ...headers, ...options.headers } });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
  return body;
}
let cursor = "";
do {
  const page = await api("/apps?limit=50" + (cursor ? `&cursor=${cursor}` : ""));
  for (const app of page.data) console.log(app.id, app.name, app.power_state);
  cursor = page.next_cursor;
} while (cursor);
import os, requests

BASE = "https://api.corehost.io/v1"
s = requests.Session()
s.headers["Authorization"] = "Bearer " + os.environ["COREHOST_TOKEN"]

def api(method, path, **kw):
    r = s.request(method, BASE + path, timeout=30, **kw)
    body = r.json()
    if not r.ok:
        raise RuntimeError(f"{r.status_code} {body['error']['code']}: {body['error']['message']}")
    return body
cursor = None
while True:
    page = api("GET", "/apps", params={"limit": 50, "cursor": cursor})
    for app in page["data"]:
        print(app["id"], app["name"], app["power_state"])
    cursor = page["next_cursor"]
    if not cursor:
        break

Get an app and its live usage

Needs the read scope. The resources object is filled in while the app is running.

curl -s https://api.corehost.io/v1/apps/$APP_ID \
  -H "Authorization: Bearer $COREHOST_TOKEN"
const BASE = "https://api.corehost.io/v1";
const headers = { Authorization: `Bearer ${process.env.COREHOST_TOKEN}` };

async function api(path, options = {}) {
  const res = await fetch(BASE + path, { ...options, headers: { ...headers, ...options.headers } });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
  return body;
}
const app = await api(`/apps/${process.env.APP_ID}`);
const r = app.resources;
console.log(app.name, app.power_state);
if (r) console.log(`cpu ${r.cpu_percent}%, memory ${(r.memory_bytes / 1e6).toFixed(0)} MB, disk ${(r.disk_bytes / 1e9).toFixed(2)} GB`);
import os, requests

BASE = "https://api.corehost.io/v1"
s = requests.Session()
s.headers["Authorization"] = "Bearer " + os.environ["COREHOST_TOKEN"]

def api(method, path, **kw):
    r = s.request(method, BASE + path, timeout=30, **kw)
    body = r.json()
    if not r.ok:
        raise RuntimeError(f"{r.status_code} {body['error']['code']}: {body['error']['message']}")
    return body
app = api("GET", f"/apps/{os.environ['APP_ID']}")
r = app["resources"]
print(app["name"], app["power_state"])
if r:
    print(f"cpu {r['cpu_percent']}%, memory {r['memory_bytes'] / 1e6:.0f} MB, disk {r['disk_bytes'] / 1e9:.2f} GB")

Restart an app

Needs the apps:write scope. The call returns 202 at once; poll the app for power_state.

curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/power \
  -H "Authorization: Bearer $COREHOST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action": "restart"}'
const BASE = "https://api.corehost.io/v1";
const headers = { Authorization: `Bearer ${process.env.COREHOST_TOKEN}` };

async function api(path, options = {}) {
  const res = await fetch(BASE + path, { ...options, headers: { ...headers, ...options.headers } });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
  return body;
}
await api(`/apps/${process.env.APP_ID}/power`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ action: "restart" }),
});
console.log("restart accepted");
import os, requests

BASE = "https://api.corehost.io/v1"
s = requests.Session()
s.headers["Authorization"] = "Bearer " + os.environ["COREHOST_TOKEN"]

def api(method, path, **kw):
    r = s.request(method, BASE + path, timeout=30, **kw)
    body = r.json()
    if not r.ok:
        raise RuntimeError(f"{r.status_code} {body['error']['code']}: {body['error']['message']}")
    return body
api("POST", f"/apps/{os.environ['APP_ID']}/power", json={"action": "restart"})
print("restart accepted")

Back up an app and get the download link

Needs the apps:write scope. A backup runs in the background, so wait for its status to be completed before you ask for the link.

# 1. start the backup and note its id
curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/backups \
  -H "Authorization: Bearer $COREHOST_TOKEN"

# 2. check it until status is "completed"
curl -s https://api.corehost.io/v1/apps/$APP_ID/backups \
  -H "Authorization: Bearer $COREHOST_TOKEN"

# 3. get a short-lived download link
curl -s https://api.corehost.io/v1/apps/$APP_ID/backups/$BACKUP_ID/download \
  -H "Authorization: Bearer $COREHOST_TOKEN"
const BASE = "https://api.corehost.io/v1";
const headers = { Authorization: `Bearer ${process.env.COREHOST_TOKEN}` };

async function api(path, options = {}) {
  const res = await fetch(BASE + path, { ...options, headers: { ...headers, ...options.headers } });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
  return body;
}
const app = process.env.APP_ID;
const backup = await api(`/apps/${app}/backups`, { method: "POST" });

let status = backup.status;
while (status !== "completed") {
  if (status === "failed") throw new Error("backup failed");
  await new Promise((r) => setTimeout(r, 10000));
  const list = await api(`/apps/${app}/backups?limit=100`);
  status = list.data.find((b) => b.id === backup.id)?.status;
}

const link = await api(`/apps/${app}/backups/${backup.id}/download`);
console.log(link.url);
import os, requests

BASE = "https://api.corehost.io/v1"
s = requests.Session()
s.headers["Authorization"] = "Bearer " + os.environ["COREHOST_TOKEN"]

def api(method, path, **kw):
    r = s.request(method, BASE + path, timeout=30, **kw)
    body = r.json()
    if not r.ok:
        raise RuntimeError(f"{r.status_code} {body['error']['code']}: {body['error']['message']}")
    return body
import time

app = os.environ["APP_ID"]
backup = api("POST", f"/apps/{app}/backups")

status = backup["status"]
while status != "completed":
    if status == "failed":
        raise RuntimeError("backup failed")
    time.sleep(10)
    page = api("GET", f"/apps/{app}/backups", params={"limit": 100})
    status = next((b["status"] for b in page["data"] if b["id"] == backup["id"]), None)

link = api("GET", f"/apps/{app}/backups/{backup['id']}/download")
print(link["url"])

Not yet available

This is the first public release, and it covers what most scripts need. These are not in the API yet, so use the dashboard for them:

  • Deploying a new app. It needs a payment check we want to get right before it can be called by a script.
  • Changing an app's environment variables and plan size.
  • Custom domains and the firewall allow-list.
  • Browsing and editing an app's files.
  • Usage history over time. Live CPU, memory and disk are on GET /apps/{app_id}.

If one of these would unblock you, tell us. It helps us decide what to build next.

Reference

Generated from the OpenAPI 3.1 description. Download the OpenAPI file to generate a client, or the Postman collection to try the calls by hand. Base URL https://api.corehost.io/v1.

Public

Catalog and regions. No token needed.

GET/catalog

List the apps you can deploy

No token needed

Parameters

currencyqueryCurrency Restrict prices to one currency.

Example

curl -s https://api.corehost.io/v1/catalog

Responses

  • 200The app catalog and plan prices.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 500Something went wrong on our side (internal_error).

Returns Catalog.

GET/regions

List regions

No token needed

Example

curl -s https://api.corehost.io/v1/regions

Responses

  • 200Every region and whether it takes new apps.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 500Something went wrong on our side (internal_error).

Account

The token you are calling with.

GET/me

Describe the calling token

Any token

Any valid token may call this; no scope is needed.

Example

curl -s https://api.corehost.io/v1/me \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200The token and the account it belongs to.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 429Too many requests (rate_limited). Wait Retry-After seconds.

Returns Me.

Apps

Your apps, their live state and power controls.

GET/apps

List your apps

Scope read

Newest first. Deleted apps are not listed. Requires read.

Parameters

limitqueryinteger
cursorquerystring The next_cursor from the previous page.

Example

curl -s https://api.corehost.io/v1/apps \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200A page of apps.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
GET/apps/{app_id}

Get one app

Scope read

Includes live resource usage when the app is running. Requires read.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.

Example

curl -s https://api.corehost.io/v1/apps/$APP_ID \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200The app.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.

Returns AppDetail.

DELETE/apps/{app_id}

Delete an app

Scope apps:delete

Requires apps:delete. Deletes the app and its data now. Billing for the app stops; the rest of your balance stays on the account. To go ahead you must repeat the app id in the query string as ?confirm={app_id}. Take a backup and download it first if you want to keep the data.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.
confirmquery, requiredstring (uuid) Must equal the app id.

Example

curl -s -X DELETE "https://api.corehost.io/v1/apps/$APP_ID?confirm=$APP_ID" \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200The app was deleted.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.
POST/apps/{app_id}/rebuild

Rebuild an app

Scope apps:write

Requires apps:write. Reinstalls the app's container and pulls its image again. Data on the app's volume is kept. The app restarts; poll GET /apps/{app_id} for power_state. Refused with 409 app_busy while an automatic update is running.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.

Example

curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/rebuild \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 202The rebuild was started.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.
POST/apps/{app_id}/power

Start, stop, restart or kill an app

Scope apps:write

Requires apps:write. The action is sent to the app at once; poll GET /apps/{app_id} for power_state. A stopped app bills at the storage-only rate until it is started again. kill stops the app without a clean shutdown; prefer stop.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.

Request body

actionrequiredstart | stop | restart | kill

Example

curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/power \
  -H "Authorization: Bearer $COREHOST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"restart"}'

Responses

  • 202The action was accepted.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.
GET/apps/{app_id}/logs

Read recent console output

Scope read

A short snapshot of the app's recent console output (the same stream the dashboard console shows), with colour codes removed. At most 6000 characters, newest at the end. Requires read.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.
max_charsqueryinteger

Example

curl -s https://api.corehost.io/v1/apps/$APP_ID/logs \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200Recent output.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.

Backups

Backups of an app's data.

GET/apps/{app_id}/backups

List an app's backups

Scope read

Newest first. Requires read.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.
limitqueryinteger
cursorquerystring The next_cursor from the previous page.

Example

curl -s https://api.corehost.io/v1/apps/$APP_ID/backups \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200A page of backups.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.
POST/apps/{app_id}/backups

Start a backup

Scope apps:write

Requires apps:write. The backup runs in the background; it is complete when its status is completed. Each app has a limited number of backup slots; when they are full you get 409 backup_limit_reached and should delete an old backup first.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.

Example

curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/backups \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 202The backup was started.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.

Returns Backup.

DELETE/apps/{app_id}/backups/{backup_id}

Delete a backup

Scope apps:delete

Requires apps:delete. Locked backups cannot be deleted (409).

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.
backup_idpath, requiredstring (uuid)

Example

curl -s -X DELETE https://api.corehost.io/v1/apps/$APP_ID/backups/$BACKUP_ID \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200The backup was deleted.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.
GET/apps/{app_id}/backups/{backup_id}/download

Get a backup download link

Scope apps:write

Requires apps:write, because a backup holds all of the app's data, including anything secret inside it. Returns a short-lived link. Fetch it soon after you get it and do not share it. Only finished backups can be downloaded.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.
backup_idpath, requiredstring (uuid)

Example

curl -s https://api.corehost.io/v1/apps/$APP_ID/backups/$BACKUP_ID/download \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200A download link.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.
POST/apps/{app_id}/backups/{backup_id}/restore

Restore a backup

Scope apps:write

Requires apps:write. Replaces the app's current data with the backup. You must send {"confirm": true}. Incomplete or near-empty backups are refused (400 backup_incomplete) so a bad archive can't wipe your app. The restore runs in the background.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.
backup_idpath, requiredstring (uuid)

Request body

confirmrequiredtrue

Example

curl -s -X POST https://api.corehost.io/v1/apps/$APP_ID/backups/$BACKUP_ID/restore \
  -H "Authorization: Bearer $COREHOST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm":true}'

Responses

  • 202The restore started.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 409The app can't do that right now: app_not_ready (still being set up), app_busy (installing, restoring or updating), backup_limit_reached, backup_locked, token_limit_reached.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.

Settings

Per-app preferences such as update behaviour.

GET/apps/{app_id}/settings

Read app settings

Scope read

Requires read.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.

Example

curl -s https://api.corehost.io/v1/apps/$APP_ID/settings \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200The app's settings.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.

Returns AppSettings.

PATCH/apps/{app_id}/settings

Change app settings

Scope apps:write

Requires apps:write. Send only the fields you want to change. auto_update can only be turned on for apps that support automatic updates (otherwise 400). Environment variables and the firewall are not available through the API yet.

Parameters

app_idpath, requiredstring (uuid) The app's id (a UUID), as shown in your dashboard URL.

Request body

auto_updateboolean
notify_on_updatesboolean
update_window_start_utcinteger, nullable UTC hour that opens the 3 hour update window. Null restores the default.

Example

curl -s -X PATCH https://api.corehost.io/v1/apps/$APP_ID/settings \
  -H "Authorization: Bearer $COREHOST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notify_on_updates":true,"update_window_start_utc":3}'

Responses

  • 200The settings after the change.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.

Returns AppSettings.

Billing

Your prepaid balance, its ledger and your invoices.

GET/billing

Balance and burn rate

Scope billing:read

Requires billing:read.

Example

curl -s https://api.corehost.io/v1/billing \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200Your balance.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 429Too many requests (rate_limited). Wait Retry-After seconds.

Returns Billing.

GET/billing/ledger

Balance ledger

Scope billing:read

Every change to your balance, newest first: top-ups, hourly usage draws, credits and refunds. Requires billing:read.

Parameters

limitqueryinteger
cursorquerystring The next_cursor from the previous page.

Example

curl -s https://api.corehost.io/v1/billing/ledger \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200A page of ledger entries.
  • 400The request was malformed (invalid_request, confirm_required, backup_incomplete, invalid_cursor).
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
GET/invoices

List invoices

Scope billing:read

One invoice per paid top-up, newest first. Invoice numbers look like CHIO- followed by random characters. Requires billing:read.

Example

curl -s https://api.corehost.io/v1/invoices \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200Your invoices.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.
GET/invoices/{number}

Get an invoice PDF link

Scope billing:read

Returns a short-lived link to the invoice PDF. Requires billing:read.

Parameters

numberpath, requiredstring An invoice number from GET /invoices.

Example

curl -s https://api.corehost.io/v1/invoices/$INVOICE_NUMBER \
  -H "Authorization: Bearer $COREHOST_TOKEN"

Responses

  • 200A link to the PDF.
  • 401Missing, malformed, expired or revoked token (unauthorized).
  • 403The token lacks the scope this call needs (insufficient_scope; details.required_scope names it), or the action is not allowed (forbidden).
  • 404No such app or backup on your account (not_found). Apps on other accounts also answer 404.
  • 429Too many requests (rate_limited). Wait Retry-After seconds.
  • 502The app platform could not be reached (upstream_unavailable). Safe to retry.

Objects

App

idstring (uuid) The app's public id.
namestring Your label for the app.
appobject
regionstring, nullable
planobject
statusprovisioning | active | suspended | deleted Lifecycle. provisioning: ordered, still being placed or set up. active: provisioned. suspended: paused for an unpaid balance. deleted: gone.
power_staterunning | starting | stopping | offline | installing | provisioning | unknown Live state of the app's container. unknown when the platform did not answer in time.
billing_modeactive | paused paused while stopped: only storage is billed.
urlstring, nullable (uri)
hostnamestring, nullable
created_atstring, nullable (date-time)

AppDetail

idstring (uuid) The app's public id.
namestring Your label for the app.
appobject
regionstring, nullable
planobject
statusprovisioning | active | suspended | deleted Lifecycle. provisioning: ordered, still being placed or set up. active: provisioned. suspended: paused for an unpaid balance. deleted: gone.
power_staterunning | starting | stopping | offline | installing | provisioning | unknown Live state of the app's container. unknown when the platform did not answer in time.
billing_modeactive | paused paused while stopped: only storage is billed.
urlstring, nullable (uri)
hostnamestring, nullable
created_atstring, nullable (date-time)
resourcesobject, nullable Live usage; null when unavailable.
notestring, nullable Human hint when the state is not plain running/offline.

AppSettings

idstring (uuid)
auto_updateboolean
notify_on_updatesboolean
update_window_start_utcinteger, nullable

Backup

idstring (uuid)
namestring, nullable
statusin_progress | completed | failed
size_bytesinteger, nullable
lockedboolean
sha256string, nullable
created_atstring, nullable (date-time)
completed_atstring, nullable (date-time)

Billing

balancenumber May be negative while an unpaid balance is in grace.
currencyCurrency
monthly_burnnumber Sum of every running app's monthly rate.
days_remaininginteger, nullable Days the balance lasts at the current burn; null when nothing is drawing.
low_balance_sincestring, nullable (date-time)
grace_daysinteger
appsobject
auto_topupobject

LedgerEntry

idstring
typestring e.g. topup, draw, welcome, admin_credit, refund, adjustment.
amountnumber Positive for money in
balance_afternumber
currencyCurrency
descriptionstring, nullable
created_atstring (date-time)
appsobject, nullable For usage draws, the amount drawn per app id.

Invoice

numberstring
doc_typestring, nullable
titlestring, nullable
issued_atstring, nullable (date-time)
paid_atstring, nullable (date-time)
currencystring, nullable
totalnumber
tax_amountnumber
tax_treatmentstring, nullable
descriptionstring, nullable
statusstring
refundedboolean

Me

tokenobject
accountobject

Region

idstring
namestring
countrystring, nullable
statusavailable | waitlist

CatalogApp

idstring
namestring
taglinestring, nullable
descriptionstring, nullable
categorystring, nullable
min_planstring, nullable
regionsarray of string
docs_urlstring, nullable (uri)
websitestring, nullable (uri)
iconstring, nullable
auto_update_availableboolean

Plan

namestring
vcpunumber
memory_mbinteger
disk_gbinteger
pricesobject Monthly price per currency.

Error

errorobject
request_idstring

Questions or something missing? Open a support ticket. Building on an open-source app we host? See the revenue share for developers.