---
title: "Domains — API v1 and MCP | Nodea Documentation"
description: "Full reference for the Domains module: REST API v1 endpoints (list, detail, set nameservers, WHOIS privacy, request EPP code), the account:* scope, MCP tools and curl / JSON-RPC examples."
url: "https://nodea.io/en/api-docs/domains"
lang: "en"
updated: "2026-07-16"
---

# Domains — API v1 and MCP | Nodea Documentation

> Domains module reference — API v1 endpoints (filtered list, detail with WHOIS contacts, set nameservers, WHOIS privacy, request EPP code), account:read/write scopes, MCP tools and examples. Registration and transfers stay web-only.

The **Domains** module covers the customer account'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.

## Customer account, ownership and visibility

Domains belong to a **customer account** (see [Customer accounts](https://nodea.io/en/api-docs/accounts)). A request acts in the current account (token pinning → X-Account-Id header → default account), and next to the token scope an account permission is required:

EndpointAccount permissionGET /domains, GET /domains/{domain}domains PATCH /domains/{domain}, POST /domains/{domain}/whois-privacy, POST /domains/{domain}/eppdomains + managedomains

The list returns only the **current account's** domains, and DomainResource carries an account\_id field. A domain outside the current account — another customer's or one of your other accounts' — and an unknown id consistently yield 404 (not 403), so the API never confirms that a foreign domain exists. A member without the permission gets 403 account\_permission\_denied with a permission field (domains or managedomains). In MCP resources outside the account map 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

MethodPathScopeDescriptionGET/api/v1/domainsaccount:readThe current account'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, cancelled (cancelled — cancelled or flagged as fraud in WHMCS; only on domains billed by WHMCS).
- 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.
- 403 — account-context error: account\_permission\_denied (the permission field names the missing flag) — domains for reads, managedomains for actions, no\_account\_context, account\_forbidden / account\_mismatch (the X-Account-Id header or token pinning) — see [Customer accounts](https://nodea.io/en/api-docs/accounts).
- 404 — unknown domain or a domain outside the current account.
- 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.

NameScopeModeDescriptiondomains-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.

**Customer accounts in MCP.** The tools act in the current account of the MCP connection (see [Customer accounts](https://nodea.io/en/api-docs/accounts)). domains-list and domain-get require the domains permission; domain-set-nameservers, domain-whois-privacy and domain-request-epp require domains + managedomains. The list covers only the current account's domains. A denial ends with the suffix (code: account\_permission\_denied, permission: \<flag\>) or (code: no\_account\_context).

### 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 } } }

## Related

- [Hosting](https://nodea.io/en/glossary/hosting)
- [Migrator — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/migrations)
- [Customer accounts — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/accounts)

---

Languages: [pl](https://nodea.pl/pl/dokumentacja-api/domains.md)  
Site index: [llms.txt](https://nodea.io/en/llms.txt)
