Nodea — logo

Hosting (DirectAdmin) — API v1 and MCP | Nodea Documentation

The Hosting module covers the user's DirectAdmin hosting accounts: listing and account detail, mailbox management, backups with restore, Let's Encrypt certificate issuance and account termination. This document describes the REST API v1 layer (/api/v1) and the module's MCP tools.

Authentication and scopes

API v1 authenticates with a Passport personal access token via the Authorization: Bearer <token> header. This module's routes are guarded by the account scope matching the operation tier:

  • account:read — listing and detail (account, mailboxes, backups).
  • account:write — mailbox create/update/delete, backup create, SSL issuance.
  • account:delete — backup restore (overwrites state) and account termination.

The whole module is additionally gated by the Pennant flag features:hosting-suite — disabling it hides the module from the web UI and the API. Rate limit: throttle:api-v1 = 240/min per token user. In MCP, each tool additionally requires the transport scope mcp:use and the hosting-suite flag.

Ownership and visibility

DirectAdmin accounts have no policy class and are never shared — every access is strictly owner-scoped by user_id. Mailboxes and backups are checked twice: the account must belong to the token user, and the mailbox/backup must belong to that account. Any mismatch yields 404 (not 403), so the API never confirms a foreign resource exists. In MCP this maps to a single "not found or not accessible" error.

Sensitive-data safety

Mailbox passwords are write-only: accepted in the create/update payload, forwarded straight to the DirectAdmin API, never stored locally and never returned in any response. The DA panel password (encrypted_password), SSO login keys, and the DA server's hostname/IP/port and API credentials are deliberately never serialized — the account exposes only its location. Internal backup columns (local_path, restore_log) are likewise not exposed.

Side effects — asynchronous and account state

DirectAdmin-side effects (backup create/restore, SSL issuance, termination) run out-of-band through queued jobs, like web — those actions return 202 Accepted. Mailbox create/update/delete calls the DA API synchronously, and the local mirror is refreshed by SyncEmailAccountsJob. Every DA-backend operation requires an active account: a pending account is still provisioning and a terminated one accepts no further actions — both return 409 Conflict.

API v1 — endpoints

MethodPathScopeDescription
GET/api/v1/hostingaccount:readOwner's hosting accounts (id DESC), paginated.
GET/api/v1/hosting/{account}account:readAccount detail: disk/bandwidth usage, SSL, location, plan, timestamps.
GET/api/v1/hosting/{account}/emailsaccount:readAccount mailboxes (by address), paginated.
POST/api/v1/hosting/{account}/emailsaccount:writeCreate a mailbox (write-only password). Returns 202.
PATCH/api/v1/hosting/{account}/emails/{email}account:writeChange password (write-only) and/or mailbox quota.
DELETE/api/v1/hosting/{account}/emails/{email}account:writeDelete a mailbox (DA + local). Returns 204.
GET/api/v1/hosting/{account}/backupsaccount:readAccount backups (id DESC), paginated.
POST/api/v1/hosting/{account}/backupsaccount:writeQueue a new backup (name backup-Y-m-d-H-i). Returns 202.
POST/api/v1/hosting/{account}/backups/{backup}/restoreaccount:deleteRestore a backup — overwrites account state (irreversible). Returns 202.
POST/api/v1/hosting/{account}/sslaccount:writeQueue a Let's Encrypt certificate. Requires an active account. Returns 202.
DELETE/api/v1/hosting/{account}account:deleteTerminate the account (destructive, async). Returns 202; already terminated → 422.

Web-only (out of API and MCP): DirectAdmin control-panel SSO login (login-key mint) and the account password reset — interactive browser hand-offs, not headless operations.

Mailboxes — body parameters

  • email_local (string, required on create, 1–64 chars, charset [a-zA-Z0-9._-]) — local part of the address.
  • domain (string, required on create, 4–253, FQDN-shaped).
  • password (string, 10–128 chars; required on create, optional on update) — write-only.
  • quota_mb (int 50–10000; required on create, optional on update).

Mailbox (HostingEmailResource): id, email, domain, quota_mb, used_mb, is_forwarder, forward_to, is_active, last_synced_at — never carries a password field. Backup (HostingBackupResource): id, backup_name, size_mb, status, is_remote, created_at, expires_at, restore_url (link to the restore action).

Error codes

  • 401 — missing/invalid token.
  • 403 — missing required scope.
  • 404 — unknown/foreign account, mailbox or backup.
  • 409 — account not active (pending/terminated) for a DA-backend action.
  • 422 — validation or terminating an already-terminated account (code already_terminated).

Examples — API

List hosting accounts

curl -s "https://app.nodea.io/api/v1/hosting?per_page=20" \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Accept: application/json"

Create a mailbox (write-only password)

curl -s -X POST https://app.nodea.io/api/v1/hosting/{account}/emails \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email_local": "office",
    "domain": "example.com",
    "password": "S3cretPass!23",
    "quota_mb": 2000
  }'

Queue a backup

curl -s -X POST https://app.nodea.io/api/v1/hosting/{account}/backups \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Accept: application/json"

Restore a backup (irreversible)

curl -s -X POST https://app.nodea.io/api/v1/hosting/{account}/backups/{backup}/restore \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Accept: application/json"

MCP — tools

The MCP tools share the authentication and scope model with API v1 (Bearer + scope in ensureScope) and the hosting-suite flag (a tool on an inactive flag disappears from tools/list). Ids are integers. A missing and a foreign id return the same error. DirectAdmin-touching actions are asynchronous (returning a queued acknowledgement). Destructive tools — hosting-backup-restore and hosting-terminate — are marked destructive and require an explicit confirm: true. Mailbox passwords remain write-only.

NameScopeModeDescription
hosting-listaccount:readread-onlyDirectAdmin accounts (id DESC); page, per_page (max 50).
hosting-getaccount:readread-onlyAccount detail by id. The DA panel password is never returned.
hosting-emailsaccount:readread-onlyAccount mailboxes (by address); account_id, page, per_page (max 50).
hosting-email-createaccount:writewriteCreate a mailbox; account_id, email_local, domain, password (write-only), quota_mb. Account must be active.
hosting-email-updateaccount:writewriteChange password (write-only) and/or quota; account_id, email_id, password?, quota_mb?.
hosting-email-deleteaccount:writewriteDelete a mailbox (DA + local); account_id, email_id. Account must be active.
hosting-backupsaccount:readread-onlyAccount backups (id DESC); account_id, page, per_page (max 50).
hosting-backup-createaccount:writewriteQueue a backup; account_id. Async — poll hosting-backups. Account must be active.
hosting-backup-restoreaccount:deletedestructive (confirm)Restore a backup — overwrites account data. Requires confirm: true; account_id, backup_id. Async.
hosting-issue-sslaccount:writewriteQueue a Let's Encrypt certificate; account_id. Async — poll hosting-get. Account must be active.
hosting-terminateaccount:deletedestructive (confirm)Terminate the account — irreversible. Requires confirm: true; account_id. Async.

Examples — MCP (JSON-RPC)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "hosting-email-create",
    "arguments": {
      "account_id": 7,
      "email_local": "office",
      "domain": "example.com",
      "password": "S3cretPass!23",
      "quota_mb": 2000
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "hosting-backup-restore",
    "arguments": { "account_id": 7, "backup_id": 128, "confirm": true }
  }
}