Nodea — logo

Domains — API v1 and MCP | Nodea Documentation

The Domains module covers the user's registered domains: filtered listing, detail with WHOIS contacts, and maintenance operations — changing nameservers, toggling WHOIS privacy, and requesting the transfer authorization (EPP) code. 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:

  • account:read — listing and detail (index, show).
  • account:write — editing nameservers and non-destructive actions (WHOIS privacy, EPP request).

The whole module is additionally gated by the Pennant flag features:hosting-suite — disabling it hides the module from both 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 same hosting-suite flag.

Ownership and visibility

Domains have no policy class and are never shared — every row access is strictly owner-scoped by user_id. A foreign or unknown id consistently yields 404 (not 403), so the API never confirms that a foreign domain exists. In MCP this maps to a single "not found or not accessible" error, preventing cross-tenant id enumeration.

Sensitive-data safety

The EPP / auth-info code is server-side write-only and is never returned by the API or MCP. It lives in an encrypted, hidden column and is populated asynchronously by TransferDomainOutJob; it can only be read from the web portal. Likewise, the WHOIS contact PESEL is encrypted and hidden — contacts return only name, company, address and the NIP/REGON identifiers, never PESEL. Internal registrar data (registrar_data) also stays hidden.

Side effects — asynchronous

Every registrar-touching operation (nameserver change, WHOIS-privacy toggle, EPP request) runs out-of-band through a queued job, exactly like the web flow: the endpoint validates input, persists local state and dispatches, and the registrar HTTP round-trip happens in the worker. That is why these actions return 202 Accepted ("job queued") rather than an immediate result.

API v1 — endpoints

MethodPathScopeDescription
GET/api/v1/domainsaccount:readOwner's domains (id DESC), paginated. Filters status, q.
GET/api/v1/domains/{domain}account:readDomain detail: nameservers, period, EPP code (null until requested) and the four WHOIS contacts.
PATCH/api/v1/domains/{domain}account:writeChange auto_renew and/or nameservers. The registrar job fires only when the effective NS set changes.
POST/api/v1/domains/{domain}/whois-privacyaccount:writeEnable/disable WHOIS privacy (enabled bool). Requires Active status. 202/422.
POST/api/v1/domains/{domain}/eppaccount:writeRequest the EPP code for an outbound transfer. Requires transfer eligibility (Active + 60 days). 202/422.

Web-only (out of API and MCP): domain availability search, ordering/registration, and inbound transfer (transfer-in) — multi-step contact-collection wizards unsuited to a headless call.

GET /domains — query parameters

  • status (enum, optional) — one of: pending, active, expired, redemption, transferred_out, transferring_in, pending_delete.
  • q (string, optional, max 255) — matches sld or tld (LIKE).
  • per_page (int 1–100, default 20), page (int ≥1).

List row (DomainResource): id, full_domain, sld, tld, status, registered_at, expires_at, days_until_expiry, auto_renew, whois_privacy_enabled. Detail (show) adds: period_years, nameservers[], epp_code (null until requested), can_transfer_out and registrant/admin/tech/billing (WHOIS contacts).

PATCH /domains/{id} — body parameters

  • auto_renew (bool, optional).
  • nameservers (array 0–4, optional) — FQDN-shaped hostnames (regex, max 253 chars). An empty array resets NS to the platform defaults.

Both keys are independent — you can change auto_renew alone or only the nameservers. UpdateDomainNameserversJob is dispatched only when the effective NS set actually changes (the same short-circuit as web).

Error codes

  • 401 — missing/invalid token.
  • 403 — missing required scope.
  • 404 — unknown or foreign domain.
  • 422 — validation or domain state: WHOIS privacy outside Active (code domain_not_active), EPP for a non-transferable domain (code domain_not_transferable).

Examples — API

List active domains

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

Change nameservers

curl -s -X PATCH https://app.nodea.io/api/v1/domains/{id} \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "nameservers": ["ns1.example.com", "ns2.example.com"]
  }'

Enable WHOIS privacy

curl -s -X POST https://app.nodea.io/api/v1/domains/{id}/whois-privacy \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'

Request the EPP code (outbound transfer)

curl -s -X POST https://app.nodea.io/api/v1/domains/{id}/epp \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Accept: application/json"
# 202 Accepted — the EPP code is not returned; read it in the web portal.

MCP — tools

The MCP tools share the authentication and scope model with API v1 (Bearer + scope checked in ensureScope) and the hosting-suite flag (a tool on an inactive flag disappears from tools/list). The domain id is an integer. A missing and a foreign id return the same error (no cross-tenant enumeration). Registrar-touching actions are asynchronous — the tool returns a queued acknowledgement, not the final result. The EPP code is never returned.

NameScopeModeDescription
domains-listaccount:readread-onlyDomain list (id DESC). Filter status (buckets: all, active, expiring = Active + expiring within 30 days, expired = expired/redemption, pending = pending/transferring_in), search q, page, per_page (max 50).
domain-getaccount:readread-onlyDomain detail with WHOIS contacts and the can_transfer_out flag. Param: id. The EPP code is not serialized.
domain-set-nameserversaccount:writeidempotentReplace nameservers (nameservers, 1–4 hostnames). Saved immediately, pushed to the registrar async. Returns the updated NS list.
domain-whois-privacyaccount:writeidempotentEnable/disable WHOIS privacy (enabled bool). Active domains only. Pushed to the registrar async.
domain-request-eppaccount:writeidempotentRequest the EPP code for an outbound transfer. Only after the ICANN 60-day lock lifts. The code is never returned — read it in the web portal.

Examples — MCP (JSON-RPC)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "domain-set-nameservers",
    "arguments": { "id": 42, "nameservers": ["ns1.example.com", "ns2.example.com"] }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "domain-request-epp",
    "arguments": { "id": 42 }
  }
}