Devani Hosting — API Reference

Deploy and manage hosted sites programmatically — create sites, attach custom domains, issue certificates, and manage storage over plain HTTP. Available on the Whitelabel Agency plan.

Authentication Conventions Account Sites Custom domains Certificates Storage SFTP Cloudflare DNS The full loop Object shapes

Authentication

Every request is authenticated with your account API key, sent as a header:

X-API-Key: dvh_your_key_here

Generate or rotate the key in your dashboard under Account → API access. The raw key is shown only once at creation — store it securely. API access requires a Whitelabel Agency plan.

Base URL for every path below:

https://app.devani.io/api/v1

Conventions

Account

GET /account

Your plan, effective limits, current usage, and the hub MCP connection URL.

curl https://app.devani.io/api/v1/account -H "X-API-Key: $KEY"
{
  "email": "you@agency.com",
  "plan": { "key": "whitelabel", "name": "Whitelabel Agency" },
  "limits": { "sites": 35, "staging": 10, "cdn_mb": 307200, "custom_domains": 35 },
  "usage":  { "sites": 4, "staging": 0, "cdn_allocated_mb": 20480 },
  "hub_mcp_url": "https://app.devani.io/api/mcp"
}

Sites

GET /sites

List every site on your account.

POST /sites

Deploy a new site. Returns when it's live on its free *.devani.studio address.

FieldTypeNotes
slugstringRequired. The subdomain label (slug.devani.studio), 3–40 chars.
display_namestringOptional. Friendly name.
custom_domainstringOptional. Attach a custom domain at creation (DNS auto-configured if you've connected Cloudflare; otherwise records are returned to add).
stagingboolOptional. Provision a staging site (counts against the staging limit, free subdomain only).
curl -X POST https://app.devani.io/api/v1/sites \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"slug":"acme","display_name":"Acme Co"}'

The response is a Site object — including devani_admin with the site's CMS login and its per-site MCP URL.

Retries are safe (idempotent). If a create is interrupted and you retry the same slug, you get the existing site back (201) instead of an error — so a dropped connection never leaves you guessing. Note: the one-time CMS password is only shown on the first successful create; a retry won't repeat it (regenerate from the dashboard if lost). A custom domain already held by one of your own sites returns a clear message naming that site.
GET /sites/{id}

Full detail for one site, including devani_admin (CMS login + MCP URL) and any pending custom-domain setup.

DELETE /sites/{id}

Permanently tear a site down (files, database, vhost, certificate, and any custom-hostname registration).

Custom domains

POST /sites/{id}/domains

Attach a custom domain. If you've connected your Cloudflare token (Whitelabel auto-DNS), the records are created for you; otherwise the records to add are returned in the site's custom_domain_setup. The domain goes live on HTTPS automatically once it validates.

FieldTypeNotes
domainstringRequired. e.g. www.client.com.
replace_dnsboolOptional. If a conflicting DNS record already exists, replace it (a live-domain cutover). Default false surfaces the conflict instead.
curl -X POST https://app.devani.io/api/v1/sites/12/domains \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"domain":"www.client.com"}'
POST /sites/{id}/domains/{domain_id}/dns?replace=true

(Re)run the automatic DNS for an attached domain — use replace=true to confirm replacing a conflicting record.

DELETE /sites/{id}/domains/{domain_id}

Detach a custom domain.

A site keeps its free *.devani.studio address as the instant URL. Once a custom domain's certificate is active it becomes the primary, and the free subdomain automatically 301-redirects to it.

Certificates

POST /sites/{id}/ssl

Issue or re-issue the certificate (e.g. after pointing a custom domain's DNS). Idempotent. Returns the updated Site.

Storage

PATCH /sites/{id}/storage-limit

Set this site's managed-storage (Devani CDN) cap. The account-wide sum of caps can't exceed your plan allowance.

FieldTypeNotes
limit_mbintegerPer-site cap in MB.

Upload file types

PATCH /sites/{id}/upload-filetypes

Choose which file types the site's media uploader accepts. Images and video are always allowed; pass a list of extra extensions to also allow them (e.g. CAD or document formats), or an empty list to return to images+video only.

FieldTypeNotes
extensionsstring[]Extra extensions, no dot (e.g. ["rfa","dwg","pdf"]). Max 40. Script/markup types (php, html, svg, js, …) are rejected for security.

Devani version & updates

GET /sites/{id}/devani

The Devani version this site runs, whether its release feed has something newer, and the backups the install holds (pre-update code backups, daily content snapshots, hourly storage snapshots).

POST /sites/{id}/devani/update

Update the site to the latest Devani release. The install's own updater runs: it backs up the current code first and preserves all content, config, and snapshots. A no-op (`updated: false`) if the site is already current. Response includes `previous_version`, `version`, and the `backup_name` it created.

SFTP

POST /sites/{id}/sftp?enabled=true

Enable chrooted SFTP (the password is returned once) or disable it (enabled=false). Re-enabling rotates the password.

Cloudflare DNS

If you've connected a Cloudflare token (Account → Automatic DNS), these routes let you read and manage the DNS on your own Cloudflare account through the same API you use for everything else — so a provisioning script never has to hold a Cloudflare key. The token stays server-side and is never returned; you authenticate with your X-API-Key only.

GET /cloudflare/zones

List the zones (domains) on your Cloudflare account. Optional ?name= filter.

curl https://app.devani.io/api/v1/cloudflare/zones -H "X-API-Key: $KEY"
{ "zones": [ { "id": "abc123", "name": "client.com", "status": "active",
              "paused": false, "name_servers": ["..."] } ] }
GET /cloudflare/zones/{zone_id}/dns

List DNS records in a zone. Optional ?type= (A, CNAME, TXT…) and ?name= filters. Returns Cloudflare's record objects (id, type, name, content, proxied, ttl).

POST /cloudflare/zones/{zone_id}/dns

Create a DNS record.

FieldTypeNotes
typestringRequired. A, AAAA, CNAME, TXT, MX, …
namestringRequired. Record name (e.g. www.client.com).
contentstringRequired. The value (IP, target, text).
proxiedboolOptional (A/AAAA/CNAME). Default false.
ttlintegerOptional. 1 = automatic.
priorityintegerOptional (MX/SRV).
curl -X POST https://app.devani.io/api/v1/cloudflare/zones/abc123/dns \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"type":"A","name":"www.client.com","content":"203.0.113.10","proxied":true}'
PATCH /cloudflare/zones/{zone_id}/dns/{record_id}

Update a record — send only the fields you want to change.

DELETE /cloudflare/zones/{zone_id}/dns/{record_id}

Delete a record by id.

Requires a connected Cloudflare token (Whitelabel Agency). These routes act only on your account's Cloudflare, scoped by whatever that token allows.

The full loop

  1. POST /sites → the site is live on its free address.
  2. It immediately appears in your hub MCP (hub_mcp_url) — point Claude at it and build it out.
  3. POST /sites/{id}/domains → attach the client's domain; with Cloudflare connected it auto-configures, otherwise add the returned records.
  4. The domain validates, the certificate issues, and it becomes primary — the free subdomain 301s to it.

Object shapes

Site

{
  "id": 12,
  "slug": "acme",
  "display_name": "Acme Co",
  "primary_domain": "www.client.com",
  "url": "https://www.client.com",
  "site_status": "active",
  "has_ssl": true,
  "setup_pending": false,
  "domains": [ { "id": 31, "name": "acme.devani.studio", "is_primary": false, "ssl": true },
               { "id": 32, "name": "www.client.com", "is_primary": true, "ssl": true } ],
  "devani_admin": {
    "url": "https://www.client.com/devani/",
    "username": "owner",
    "password": "shown once at creation",
    "mcp_url": "https://www.client.com/devani/mcp/?api_key=..."
  },
  "custom_domain_setup": []
}

Devani Hosting · app.devani.io