Namecheap MCP connects your AI assistant (such as Claude) to your Namecheap account. Through it, the assistant can check and register domains, hand you a link to buy a domain on Namecheap's own site instead, manage domain contacts, and read or edit DNS records on your behalf — you just ask in plain language, and the assistant calls the right tools.
You need a Namecheap account. Namecheap MCP is available at https://mcp.namecheap.com/mcp.
How you connect depends on your AI assistant:
https://mcp.namecheap.com/mcp. Anthropic's Claude is the client we have verified this to work with.https://mcp.namecheap.com/mcp. Other clients may work, but we haven't verified them yet.When you connect, you'll be asked to sign in to Namecheap and grant the assistant access to your account. Which tools the assistant can use depends on the access you approve — if a tool is not available because access wasn't granted, see Access you grant, then reconnect and approve the access it needs.
The agreements and policies that apply to Namecheap MCP and to anything you buy through it:
Namecheap registrations complete synchronously, so there is no background-operation tool to poll. domain_register buys a domain in the conversation, paid with a saved payment method or Namecheap account funds, and is the preferred way to buy. domain_purchase_link returns a link the customer opens and pays on themselves, choosing their own payment method on namecheap.com, and domains_list is what confirms the outcome afterwards.
| Tool | What it does | Access |
|---|---|---|
contacts_save
|
Save contact details and get a contact ID | Write |
contacts_get
|
Read a saved contact by its ID | Read |
contacts_list
|
List all saved contacts to find and reuse one | Read |
domains_list
|
List your domains, or look up one domain | Read |
domains_check_availability
|
Check if domains are available to register | Read |
domain_register
|
Register (buy) a domain — spends money | Sensitive write |
domain_purchase_link
|
Get a link for the customer to buy a domain on namecheap.com's payment page — nothing is charged by the call | Write |
domain_set_contacts
|
Assign contacts to a domain you own | Sensitive write |
domain_set_nameservers
|
Switch a domain to basic or custom nameservers | Sensitive write |
dns_records_get
|
Read DNS records for a domain | Read |
dns_records_save
|
Add DNS records or update their TTL | Write |
dns_records_delete
|
Delete DNS records | Sensitive write |
Access: Read only retrieves information. Write changes data in a way you can easily change back. Sensitive write has an effect you cannot simply take back (spends money, cannot be undone, immediately changes how your domain works); your assistant should ask you to confirm every call.
The Namecheap sign-in page can show a consent screen with a choice between read access and read-and-write access; read access is always included and write access is a separate choice. Where it does, the access you consent to decides which tools the assistant can use:
| You allow | The assistant can use |
|---|---|
| Read | The tools marked Read above: contacts_get, contacts_list, domains_list, domains_check_availability, and dns_records_get. |
| Read and write | Every tool in the table above. |
-32602 with the message Tool <name> not found. The answer never says why.Wherever a contact is required (domain_register, domain_set_contacts), each role accepts either:
firstName, lastName, email, …). It is saved to your Namecheap account automatically; you do not have to call contacts_save first.{ "contactId": "…" } pointing at a contact you saved earlier with contacts_save, or one you found with contacts_list.A contactId is an opaque string (1–32 characters). Do not parse it or assume a format — just pass it back where a contact is accepted. You may also reuse a contactId from a contacts_list result, so look up the contacts already saved on your account before entering new details.
Several tools are designed to be used together: the output of one becomes the input of the next.
domain_register is the preferred way to buy a domain: it registers the name inside the conversation and charges the customer's default payment method (the one marked "Default for apps" in their Namecheap account, which may be account funds), or account funds when there is no usable default, after an explicit confirmation step. The customer can pick any other saved method or account funds instead. On a purchase request, use it — don't ask the customer to choose between it and the purchase link. To let the customer check out on namecheap.com themselves, see Buying through the purchase link below.
domains_check_availability
— check the name(s) you want. Only proceed when result is available. Each available name includes the USD price to register it (standard and premium alike), or priceUnavailableReason when it can't be determined, plus minRegisterPeriodInYears and maxRegisterPeriodInYears — the term that TLD allows. Note the price covers price.pricedYears years, which is the TLD's shortest allowed term and is not always 1.domain_register
(preview) — call with confirmationToken unset to get status: confirmation_required, a fresh confirmationToken, and the price that will be charged. Nothing is billed. Omit paymentMethodId to let the tool propose the account's default payment method (the one marked "Default for apps", which may be account funds; or account funds when there is no usable default) — the response then also carries paymentMethods so a different one can be chosen; pass a specific id as paymentMethodId to charge that method instead. The tool's response text is a full confirmation — seller, term, price breakdown, the renewal price and term with the expected first renewal date (when auto-renew is on), auto-renew, WHOIS privacy, payment source, the registrant/admin/tech/billing contacts, links to the terms, and the refund policy (domain registrations and renewals are non-refundable and can't be cancelled) — show it to the user as-is. Pick years between minRegisterPeriodInYears and maxRegisterPeriodInYears from step 1 — an out-of-range value is rejected outright. Contacts can be passed inline here, or as { contactId } if you saved them with contacts_save. Before entering new details, look up the account's saved contacts with contacts_list and reuse one by its contactId.domain_register
(accept/decline) — after the user agrees, call again with the exact same arguments plus that confirmationToken and confirmationResponse: "accept". This charges the resolved payment method and is irreversible. It returns the outcome directly (no polling): usually status: completed with the amount charged, or status: pending while the registry finishes. Both carry a purchase receipt as the response text — show it to the user as-is — plus a flat orderId and nextRenewal. To cancel instead, call again with the same confirmationToken and confirmationResponse: "decline" — nothing is billed. If the price can't be determined on either call (with auto-renew on, that includes the renewal price), the tool returns status: price_unavailable instead and nothing is charged. If no payment method can be used, it returns status: payment_unavailable with the account's saved methods and nothing is charged. A purchase the domains service refuses is returned as an error, and one whose outcome cannot be established fails with infrastructure.outcomeUnknown (see domain_register).domains_list
— if the result was pending, call domains_list for the domain shortly to confirm it is registered.domains_check_availability --> domain_register --> domain_register --> (domains_list if pending)
(available? + price + (token unset: (token + accept: confirm final state
min/maxRegisterPeriod) confirmation, completed/pending)
confirmationToken,
no charge)
domain_purchase_link hands the customer a link to Namecheap's own consent and payment page, where they approve and pay themselves. Because they pick their payment method there, it is the route when the customer wants to check out on namecheap.com themselves rather than in the conversation — for example after a domain_register result of payment_unavailable that asks them to complete the purchase on the website — or when the customer or the client simply asks for a link. The consent page has no field for term, WHOIS privacy or contacts, so none of those can be set this way.
Never hand out the link when a purchase attempt's outcome is unknown. A domain_register call that fails with infrastructure.outcomeUnknown means the registration may have completed and billed. A purchase link there risks charging the customer a second time for a domain they already bought. Call domains_list for the domain first, and offer the link only if it is genuinely not registered.
domains_check_availability --> domain_purchase_link --> (domains_list to confirm)
(available? + price) (consentUrl the customer after the customer says
opens and pays on they've completed checkout
namecheap.com; no charge
from this call)
contacts_list
— look up the contacts already saved on the account and pick one to reuse (optional — inline details work too).domain_set_contacts
— assign contacts to the domain, inline or by { contactId }, reusing a contactId from contacts_list where one fits. Completes immediately. verificationStatus is always null for Namecheap. Some TLD registries require extended attributes — pass them via tldAttributes.domains_list
— find the domain and see its current nameservers ({ provider, hosts }; populated when fetching a single domain).domain_set_nameservers
— switch it to Namecheap's default nameservers with provider: "basic" (no hosts), or point it at your own with provider: "custom" and a list of 2–12 hosts. It returns the resulting { provider, hosts }. Re-applying the state a domain is already in is not an error: nothing is written, and the answer carries the note "Already set; nothing changed.".domains_list
— find the domain you want to manage (or pass its name directly if you know it).dns_records_get
— read the current records for the domain.dns_records_save
or dns_records_delete — add, update, or remove records. Records returned by dns_records_get have the same shape the save and delete tools accept (delete just omits ttl), so the assistant can read, adjust, and write back. Matching is case-insensitive except for TXT records, which are case-sensitive. MX records are saved only while the domain's Mail Settings are Custom MX; otherwise the tool refuses them (or, with force, lists them in failed) and says to switch Mail Settings to Custom MX in the Namecheap dashboard.domains_list
— page through all your domains with sorting, or fetch a single domain by name. See the tool below for which fields Namecheap reports.contacts_list
— page through all the contacts saved on your account to find and reuse an existing one (by its contact ID) instead of creating a duplicate.contacts_get
— look up the details behind any contact ID you see on a domain or in a contacts_list result.Every tool returns its result as structured JSON, and every tool completes immediately — Namecheap has no long-running background operations.
Contacts are the people or organizations attached to a domain registration (registrant, admin, tech, billing). A contact is referenced everywhere by its contact ID — an opaque string.
contacts_save — Save ContactAccess: Write.
Saves contact details and returns the generated contact ID. Validation of some fields (such as stateProvince and postalCode) depends on the selected country.
Idempotent. Saving details identical to a contact you saved before reuses that existing contact ID instead of creating a duplicate.
If the call fails with infrastructure.serviceUnavailable, the contact may still have been saved: look for it with contacts_list before saving again (see Retrying writes).
| Parameter | Required | Type & constraints |
|---|---|---|
firstName
|
Yes | String, 1–64 chars. May include hyphens and apostrophes. |
lastName
|
Yes | String, 1–64 chars. May include hyphens and apostrophes. |
email
|
Yes | Valid email address, max 254 chars. |
address1
|
Yes | Address line 1. String, 1–128 chars. |
city
|
Yes | String, 1–64 chars. |
country
|
Yes | Two-letter country code (ISO 3166-1 alpha-2), e.g. US. |
phone
|
Yes | International format +CountryCode.Number, e.g. +1.2025551234. Max 32 chars. |
organization
|
No | Organization/company name. 1–128 chars. |
address2
|
No | Address line 2. 1–128 chars. |
stateProvince
|
No | State/province name, 1–64 chars. May be required depending on the country. |
postalCode
|
No | 1–16 chars. May be required depending on the country. |
phoneExt
|
No | Phone extension, 1–16 chars. |
fax
|
No | Fax number, same +CountryCode.Number format, max 32 chars. |
taxNumber
|
No | Tax number, 1–32 chars. |
Returns
{ "contactId": "..." }
contactId (opaque, 1–32 chars) is what you pass to domain_register, domain_set_contacts, and contacts_get; contacts_list returns the ids of everything you have saved.
When Namecheap's address validation proposes a corrected address, the result may additionally include an addressSuggestion:
{
"contactId": "...",
"addressSuggestion": {
"status": "corrected",
"suggestedAddress": {
"address1": "...", "address2": "...", "city": "...",
"stateProvince": "...", "country": "...", "postalCode": "..."
}
}
}
Each field of suggestedAddress is present only when Namecheap proposes a value for it. Treat it as a suggestion to show the user, not an automatic change — the contact is still saved with the details you submitted.
contacts_get — Get ContactAccess: Read.
Reads the details of a saved contact by its contact ID. Contact IDs come from a contacts_list result or from saving a contact.
| Parameter | Required | Type & constraints |
|---|---|---|
contactId
|
Yes | Opaque contact ID, 1–32 chars. |
A contactId that is not numeric, or that belongs to another account, returns business.notFound ("Contact not found in this account.").
Returns — { contact } with:
| Field | Type |
|---|---|
firstName, lastName, email, address1, city, country, phone, postalCode |
String |
organization, address2, stateProvince, phoneExt, fax, faxExt, taxNumber |
String or null |
contacts_list — List ContactsAccess: Read.
Lists all contacts saved under your account, so you can find and reuse an existing contact (by its contact ID) instead of creating a duplicate. The list is paginated and sortable, consistent with domains_list.
| Parameter | Required | Type & constraints |
|---|---|---|
take
|
No | Items per page, 1–100. Default 10. |
skip
|
No | Items to skip, 0 or more. Default 0. |
orderBy
|
No | Up to 8 sort keys: name, email, organization; prefix with - for descending (e.g. -name). Without orderBy, contacts come back in the order your account stores them. |
Returns — { items, total } where total is the number of contacts saved on the account (not the page size), and each item carries enough to tell contacts apart without a follow-up call:
| Field | Type |
|---|---|
contactId
|
String (opaque). Pass to contacts_get, domain_set_contacts, or domain_register. |
name
|
String — the contact's name. |
email
|
String or null when the contact has no email on record. |
organization
|
String or null when the contact has no organization on record. |
{
"items": [
{ "contactId": "424242", "name": "Ada Lovelace", "email": "ada@example.com", "organization": "Analytical Engines" }
],
"total": 1
}
Domain name inputs (domain/domainName) accept Unicode (IDN) or ASCII (A-label) — either way, the tool normalizes the name to punycode automatically before use. domains_check_availability and domain_register additionally require a TLD Namecheap supports for registration: a domain whose TLD isn't supported is treated as not available rather than checked or charged. The other domain tools (domains_list, domain_purchase_link, domain_set_contacts, domain_set_nameservers) and the DNS tools normalize the name only and never reject on TLD support.
domains_list — List DomainsAccess: Read.
Retrieves a paginated list of your domains. Pass domain to fetch a single domain by name instead (pagination and ordering are then ignored, and the result includes a note saying so if they were supplied). A domain that is not in your account, whether unregistered or registered by someone else, is not an error: the result is { "items": [], "total": 0, "note": "This domain is not in this account." }, the same for both cases.
| Parameter | Required | Type & constraints |
|---|---|---|
domain
|
No | Fully qualified domain name to fetch a single domain. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
take
|
No | Items per page, 1–100. Default 10. |
skip
|
No | Items to skip, 0 or more. Default 0. |
orderBy
|
No | One sort key: name, unicodeName, registrationDate, or expirationDate; prefix with - for descending (e.g. -expirationDate). unicodeName sorts the same way as name. Any other value, or a second key, is rejected with application.request.badRequest. |
Returns — { items, total } where each item describes a domain:
| Field | Meaning |
|---|---|
name / unicodeName |
Domain name. Namecheap reports unicodeName as a mirror of name (no distinct Unicode form). |
isPremium
|
Whether the domain is a premium name. |
autoRenew
|
Whether auto-renew is enabled — see the note below on when this is reported. |
registrationDate / expirationDate |
Registration and expiration timestamps. |
lifecycleStatus
|
creating, registered, grace1, or redemption. |
verificationStatus
|
Not reported by Namecheap — always null. |
eppStatuses
|
Not reported by Namecheap — empty. |
suspensions
|
Not reported by Namecheap — empty. |
privacyProtection
|
{ level: "public" | "high", contactForm: boolean }. |
nameservers
|
{ provider: "basic" | "custom", hosts: [...] }. See the note below. |
contacts
|
Not reported by Namecheap — comes back empty. Use contacts_list to find your saved contacts, or contacts_get if you already hold a contact ID. |
contactDetails
|
Present only when fetching a single domain via domain: the full contact details assigned to the domain, per role (registrant, admin, tech, billing). See the note below. |
Namecheap-specific reporting:
contacts, eppStatuses, suspensions, and verificationStatus are not reported — they come back empty or null.unicodeName always mirrors name.nameservers is populated only when fetching a single domain via domain. In the multi-item list it reports the basic default with no hosts.contactDetails is populated only when fetching a single domain via domain. It carries the actual registrant/admin/tech/billing contact details assigned to that domain, so you can read them without holding a contact ID. The id-based contacts field still comes back empty even in the single-domain case, and contactDetails is not included in the multi-item list.autoRenew is a real true/false only in the multi-item list. Fetching a single domain via domain returns autoRenew: null, because Namecheap's single-domain lookup carries no auto-renew field — reported as null rather than a misleading false.domains_check_availability — Check Domain AvailabilityAccess: Read.
Checks whether one or more domain names are available to register. Names are checked in groups of up to 5, a few groups at a time. A domain whose TLD isn't supported for registration is not sent to the availability check at all — it's returned immediately as tldNotSupported.
| Parameter | Required | Type & constraints |
|---|---|---|
domains
|
Yes | 1–20 fully qualified domain names. Each accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
Returns — { results }, one entry per requested name:
| Field | Meaning |
|---|---|
domain
|
The checked name. |
result
|
available, taken, invalidDomainName, tldNotSupported, unexpectedError, or checkTimedOut. |
premiumPricing
|
For premium names: list of { operation, price, currency } where operation is register, transfer, renew, or restore. Empty for regular names. |
price
|
For available names (standard and premium): the USD price to register the domain for the shortest term the TLD allows — { amount, currency: "USD", pricedYears?, pricePerYear?, icannFee?, isPremium }. amount is the payable total for that whole term; pricedYears states how many years it covers. No pre-discount or "was" price is reported. icannFee is the ICANN fee (USD) already included in amount, returned separately so the breakdown can be explained; it appears only when the TLD carries a fee. |
pricePerYear
|
Inside price: amount divided by pricedYears, so a yearly figure is always available for comparison. When pricedYears is 1 it is the real one-year price; above that it is a per-year average of the term, not a term you could buy. Omitted for premium names, whose quote carries no term. |
minRegisterPeriodInYears / maxRegisterPeriodInYears |
For available names: the shortest and longest registration period that TLD actually permits, as two plain numbers. Use them to pick a valid years for domain_register. Both omitted when the allowed period could not be determined. |
priceUnavailableReason
|
Present instead of price when the price could not be determined for an available name. The check itself still succeeds. |
Only available names are priced; taken/invalid results carry neither price nor priceUnavailableReason.
checkTimedOut
means the check for that name did not finish in time, so nothing is known about it — it is not taken and not available. It carries no price. Names are checked in groups, and only the names of a group that timed out come back this way; the other results are valid. Check just the checkTimedOut names again later. The call fails with an error only when every group timed out, or when a group fails for any other reason.
Most TLDs allow one year, but some do not. .ai, for example, has a two-year minimum. For those, price.amount is the total for the minimum term — not a one-year price you could act on — and price.pricedYears says so:
{
"domain": "example.ai",
"result": "available",
"premiumPricing": [],
"price": { "amount": 79.98, "currency": "USD", "pricedYears": 2, "pricePerYear": 39.99, "isPremium": false },
"minRegisterPeriodInYears": 2,
"maxRegisterPeriodInYears": 10
}
pricePerYear is present here — 79.98 divided by the two years it covers gives 39.99. This is the total divided by the term, not a price you could pay for a single year (a one-year .ai registration cannot be bought). Always show amount together with pricedYears ("$79.98 for 2 years"), never amount alone. For an ordinary TLD, pricedYears is 1 and pricePerYear equals amount.
Namecheap-specific reporting
minRegisterPeriodInYears/maxRegisterPeriodInYears come from Namecheap's own TLD catalogue. Where that catalogue understates a minimum, the gateway corrects it from what the pricing service will actually sell: the first affected request may report the catalogue's value, and every request after it reports the corrected one.pricePerYear is never reported for a premium name — a premium quote carries no term, so a per-year figure would be invented.checkTimedOut only after both attempts failed.domain_register — Register DomainAccess: Sensitive write — spends money.
Registers (buys) a domain. This charges a payment method — your default method (marked "Default for apps" in the account), account funds, or one you choose — and is irreversible. This is the preferred way to buy a domain — on a purchase request use this tool rather than offering the customer a choice of purchase paths. Recommended sequence: domains_check_availability -> domain_register. A domain whose TLD isn't supported for registration is rejected immediately — before any availability check, pricing, or charge.
years must be within the TLD's own allowed period. The 1–10 bound below is the outer limit across all TLDs; each TLD is narrower. .ai allows 2–10, .co and .io allow 1–5, .sg 1–2, .fr exactly 1. A years outside that range is rejected with a validation error naming the allowed range — before any availability check, pricing or charge — and the value is not quietly adjusted for you:
.ai domains cannot be registered for 1 year: this TLD allows 2–10 years. Call domains_check_availability for this domain to see its allowed registration period.
Read minRegisterPeriodInYears/maxRegisterPeriodInYears from domains_check_availability first and pick a years inside it. The same check re-runs on the confirming (confirmationResponse: "accept") call, so it can never be bypassed by confirming.
Contacts. Each contact role takes inline details or a { contactId } reference. You may also reuse a contactId from a contacts_list result, so look up the contacts already saved on the account and reuse one before entering new details.
Two-step pre-charge confirmation. Call first with confirmationToken unset: the tool prices the domain fresh, resolves the payment method, builds a full confirmation — the seller (for example "Namecheap (Namecheap, Inc.)"), term, price breakdown (including any ICANN fee and whether the domain is premium), the renewal price and how often it renews (yearly for most domains; every 2 years for a domain such as .ai that only renews for 2 years at a time) the expected first renewal date, and that it is charged to your default payment method (only when auto-renew is on; the line reads, for example, "Renews yearly at 12.98 USD on 2027-10-16 (expected), charged to your default payment method." or "Renews every 2 years at 229.96 USD on 2028-10-16 (expected), …" — the date is today (UTC) plus the registration term, not the renewal period, an estimate in the same YYYY-MM-DD form the receipt uses, and it is also returned as structuredContent.disclosure.renewal.date), auto-renew, WHOIS privacy, payment source, the registrant/admin/tech/billing contacts (a contact identical to the registrant is shown as "same as registrant"), links to the Universal Terms of Service and the Domain Registration Agreement, and the refund policy (domain registrations and renewals are non-refundable and can't be cancelled) — and returns status: "confirmation_required" with that price and a fresh confirmationToken. Nothing is registered or billed on this call. The full confirmation is the tool's response text; show it to the user as-is. The seller, renewal, terms and refund policy are also returned in structuredContent.disclosure (below), so a client that passes only the structured result to the model still has them. Once they agree, call again with the exact same arguments plus this confirmationToken and confirmationResponse: "accept" to submit the purchase, or confirmationResponse: "decline" to cancel it — nothing is billed on a decline. The token is bound to these exact arguments, the quoted price, and the resolved payment method, and expires after a short time: a missing, expired, tampered, or no-longer-matching token on the confirming call simply returns a brand-new confirmation with a fresh token — never an error, never a charge. If the price cannot be determined on either call (with auto-renew on, that includes the renewal price, which must answer within 3 seconds), the tool returns status: "price_unavailable" instead of a token and never charges; retry later.
Choosing a payment method. Omit paymentMethodId to charge the account's default payment method — the one marked "Default for apps" in the Namecheap account, which may be account funds — or account funds when there is no usable default; the confirmation names the resolved source in paymentSource and its response text, and account funds are only chosen when the balance, in the price's currency, covers the price. When it's omitted, the response also carries paymentMethods: the account's saved cards and account funds with its balance, each with an id and a display label (e.g. "Visa ···1234" or "Account funds (USD 17.29)"), so the customer can pick a different one — pass that id back as paymentMethodId on the next call to charge it instead. A requested id that isn't on the account, an expired method, or one that can't be charged for this purchase (e.g. flagged as not usable for an unattended charge), is rejected with a validation error naming the reason — no token is minted and nothing is charged. If no payment method can be used at all — no saved method is usable and account funds don't cover the price, or the saved methods couldn't be read — the tool returns status: "payment_unavailable" with the reason and the account's saved methods, and never charges; the customer needs to choose another method, add one, or add funds. Adding, editing or removing payment methods is not something this tool does — the customer manages them in their Namecheap account.
Synchronous. Unlike some registrars, Namecheap registration completes as part of the call — there is no operation to poll. A confirmed call returns its outcome directly (see the status table below).
| Parameter | Required | Type & constraints |
|---|---|---|
domain
|
Yes | Fully qualified domain name to register, e.g. example.com. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
years
|
Yes | Registration period in years. 1–10 is the outer bound; the accepted range is the TLD's own — see minRegisterPeriodInYears/maxRegisterPeriodInYears from domains_check_availability. Out-of-range values are rejected, not adjusted. |
autoRenew
|
Yes | Boolean. When true, the domain renews automatically at expiration using the account's default payment method. Auto-renew is turned on right after the domain is registered and charged, and is reported separately in the result (see below). |
privacy.level
|
Yes |
high hides the registrant's contact details from public WHOIS; public publishes them. |
privacy.userConsent
|
Yes | Boolean. Must confirm you agree to the selected privacy setting. |
contacts.registrant
|
Yes | Inline contact details or { contactId } (an id from contacts_save or contacts_list). |
contacts.admin
|
Yes | Inline contact details or { contactId } (an id from contacts_save or contacts_list). |
contacts.tech
|
Yes | Inline contact details or { contactId } (an id from contacts_save or contacts_list). |
contacts.billing
|
Yes | Inline contact details or { contactId } (an id from contacts_save or contacts_list). |
tldAttributes
|
No | Extended attribute values keyed by name, required only for certain TLDs (e.g. registrant nexus or legal-type fields). Provide them for TLDs that need them; omit otherwise. |
paymentMethodId
|
No | String, 1–64 chars. Id of a saved payment method to charge, taken from a previous call's paymentMethods list. Omit to accept the account's default payment method (the one marked "Default for apps", which may be account funds; or account funds when there is no usable default). |
confirmationToken
|
No | String, up to 4096 chars. Server-issued token returned by a previous domain_register call for these exact arguments. Omit on the first call for a new registration attempt. Expires after a short time and is bound to the exact arguments, price, and payment method it was issued for — resend it unchanged, together with confirmationResponse, to act on it. |
confirmationResponse
|
Conditional |
"accept" or "decline". Only meaningful together with a valid confirmationToken. "accept" submits the (billed) registration shown in that confirmation; "decline" cancels it without charging. Omit on the first call. |
Returns — after the first call (nothing charged):
{
"domain": "example.com",
"years": 1,
"status": "confirmation_required",
"price": {
"amount": 9.08,
"currency": "USD",
"pricedYears": 1,
"pricePerYear": 9.08,
"icannFee": 0.2,
"isPremium": false
},
"confirmationToken": "<opaque confirmation token>",
"paymentSource": "Visa ···1234",
"disclosure": {
"seller": { "name": "Namecheap", "legalEntity": "Namecheap, Inc.", "summary": "Namecheap (Namecheap, Inc.)" },
"renewal": {
"price": { "amount": 12.98, "currency": "USD" },
"years": 1,
"date": "2027-10-16",
"estimated": true,
"summary": "Renews yearly at 12.98 USD on 2027-10-16 (expected), charged to your default payment method."
},
"terms": [
{ "label": "Universal Terms of Service", "url": "https://www.namecheap.com/legal/universal/universal-tos/" },
{ "label": "Domain Registration Agreement", "url": "https://www.namecheap.com/legal/domains/registration-agreement/" }
],
"refundsAndCancellation": "Domain registrations and renewals are non-refundable and can't be cancelled."
},
"note": "Nothing has been charged yet. Show the confirmation to the user, including the seller, renewal, terms and refunds and cancellation from `disclosure`, and once they agree, call domain_register again with this confirmationToken and confirmationResponse=\"accept\" to complete the purchase, or confirmationResponse=\"decline\" to cancel."
}
disclosure carries the same pre-purchase disclosure as the response text, in the same words, for clients that hand the model only structuredContent: seller (name, legalEntity and summary, the Seller line, for example "Namecheap (Namecheap, Inc.)"), renewal (present only when auto-renew is on: price { amount, currency } for one renewal, years the renewal term, for example 2 for a domain such as .ai, date and estimated: true for the expected first renewal date when it is known, and summary, the Renewal line), terms ([{ label, url }], the Universal Terms of Service and the Domain Registration Agreement) and refundsAndCancellation (the refund sentence). Show all of it to the user before they confirm.
Returns — after the first call with paymentMethodId omitted, additionally carrying the account's payment methods so the customer can pick a different one:
{
"domain": "example.com",
"years": 1,
"status": "confirmation_required",
"price": {
"amount": 9.08,
"currency": "USD",
"pricedYears": 1,
"pricePerYear": 9.08,
"icannFee": 0.2,
"isPremium": false
},
"confirmationToken": "<opaque confirmation token>",
"paymentSource": "Visa ···1234",
"paymentMethods": [
{ "id": "a81c5e02-94d3-4f7b-b6a0-13e9d2c47f58", "type": "CreditCard", "order": 0, "label": "Visa ···1234" },
{ "id": "3f2a9c1e-7b64-4d0a-9e51-2c8f6a1b0d47", "type": "Funds", "order": 1, "label": "Account funds (USD 17.29)", "availableBalance": 17.29, "balanceCurrency": "USD" }
],
"note": "Nothing has been charged yet. Show the confirmation to the user, including the seller, renewal, terms and refunds and cancellation from `disclosure`, and once they agree, call domain_register again with this confirmationToken and confirmationResponse=\"accept\" to complete the purchase, or confirmationResponse=\"decline\" to cancel."
}
Alongside this JSON, the tool's response text is the full confirmation to show the user — it restates the domain, term, and price above, plus lines for Auto-renew: on/off, WHOIS privacy: on/off, Payment source: <resolved method label>, and each of the registrant/admin/tech/billing contacts (name, email, country — a contact matching the registrant reads "same as registrant"), followed by instructions for the next call. A paymentMethods entry never carries card numbers, cardholder names, or any billing or issuer detail beyond label — isExpired (present whenever a card's expiry could be read) and offSessionForbidden (present only when true) flag a method as not currently usable when true, and availableBalance/balanceCurrency are present only for the account funds entry. For a multi-year term, price.amount is the total for the whole term and price.pricePerYear is that total divided by the term — e.g. years: 5 on .com returns { "amount": 71.40, "pricedYears": 5, "pricePerYear": 14.28 }, and example.ai with years: 2 returns { "amount": 79.98, "pricedYears": 2, "pricePerYear": 39.99 }.
Returns — after confirmationResponse: "accept" (registered and charged):
{
"domain": "example.com",
"years": 1,
"status": "completed",
"orderId": "123456",
"charged": { "amount": 9.08 },
"operation": { "kind": "order", "id": 123456 },
"nextRenewal": { "date": "2027-10-13", "price": { "amount": 10.98, "currency": "USD" }, "estimated": true },
"price": {
"amount": 9.08,
"currency": "USD",
"pricedYears": 1,
"pricePerYear": 9.08,
"icannFee": 0.2,
"isPremium": false
},
"note": "..."
}
The tool's response text for a completed registration is the purchase receipt to show the user as-is:
Your purchase is confirmed. example.com is registered. Order ID: 123456 Domain: example.com Term: 1 year Charged: 9.08 USD Payment method: Visa ···1234 Next renewal (expected): Renews on 2027-10-13 at 10.98 USD Need help? Contact support: https://www.namecheap.com/help-center/ Refund Policy: https://www.namecheap.com/legal/general/refund-policy/
A pending registration returns the same structure with neutral wording — the amount is labelled Order total, nothing says the purchase is confirmed, and the last lines before the links point to domains_list:
Your order is pending. It has been received and is still being processed. Order ID: 123456 Domain: example.com Term: 1 year Order total: 9.08 USD Payment method: Account funds Renewal (expected): Expires on 2027-10-13; renewal price 10.98 USD Call domains_list for this domain shortly to see its final state. Need help? Contact support: https://www.namecheap.com/help-center/ Refund Policy: https://www.namecheap.com/legal/general/refund-policy/
Notes on the receipt:
orderId is the NCPL order id as a string (the same value as operation.id); it is present when NCPL returned one. nextRenewal is { "date": "YYYY-MM-DD", "price": { "amount", "currency" }, "estimated": true }; price is left out when it cannot be determined, and the whole field when the date cannot be. Each key is absent rather than null. estimated is always true here, because the date is computed rather than read from the registry.nextRenewal.price carries the same amount without the period. With auto-renew off, nothing is bound: the receipt reads "Expires on <date>; renewal price <price>" with the one-year renewal rate (for a standard TLD the renewal rate from Namecheap's price list, for a premium domain the premium renewal price from the availability check), asked for once after the confirmation is accepted and before the charge, and left out if it is unavailable or does not answer within 3 seconds.Visa ···1234 (three middle dots) for a saved card, or Account funds; a funds balance never appears on the receipt.note.payment_unavailable result keeps its note as the text and adds a final support line (Need help? Contact support: https://www.namecheap.com/help-center/). It carries no orderId or nextRenewal. A purchase that fails is an error, and its detail ends with the same support line.
Returns — after confirmationResponse: "decline" (nothing charged):
{
"domain": "example.com",
"years": 1,
"status": "cancelled",
"price": { "amount": 9.08, "currency": "USD", "pricedYears": 1, "pricePerYear": 9.08, "icannFee": 0.2, "isPremium": false },
"note": "Registration of example.com was not submitted because the purchase was not confirmed."
}
Returns — if the price can't be determined, on either call:
{
"domain": "example.com",
"years": 1,
"status": "price_unavailable",
"priceUnavailableReason": "Price is currently unavailable for this domain.",
"note": "Registration of example.com could not be priced right now, so nothing was confirmed or charged. Try again shortly."
}
Returns — if no payment method can be used, on either call:
{
"domain": "example.com",
"years": 1,
"status": "payment_unavailable",
"price": {
"amount": 9.08,
"currency": "USD",
"pricedYears": 1,
"pricePerYear": 9.08,
"icannFee": 0.2,
"isPremium": false
},
"paymentMethods": [
{ "id": "a81c5e02-94d3-4f7b-b6a0-13e9d2c47f58", "type": "CreditCard", "order": 0, "label": "Visa ···1234", "isExpired": true },
{ "id": "3f2a9c1e-7b64-4d0a-9e51-2c8f6a1b0d47", "type": "Funds", "order": 1, "label": "Account funds (USD 4.10)", "availableBalance": 4.1, "balanceCurrency": "USD" }
],
"refusalReason": "insufficient_funds",
"note": "example.com was not registered and nothing was charged. Your account funds (USD 4.10) do not cover the USD 9.08 price. Add funds to the account or choose a saved payment method."
}
refusalReason names the refusal in a fixed vocabulary a client can branch on: insufficient_funds, currency_mismatch, no_usable_method, payment_method_expired, payment_method_unsupported, payment_declined, cardholder_confirmation_required, charge_failed or temporarily_unavailable (the saved methods could not be read, or the card provider could not process the charge or verify the method right now). More values may be added, so treat a value you do not know as a general payment refusal and show the note.
Charge refused for a payment reason. The same payment_unavailable status is returned, with no token, when the charge itself is refused for a recognised payment reason after the customer accepted. The note carries fixed guidance for the cause, refusalReason names it, and paymentMethods lists the account's methods again when they can be read:
| Cause |
refusalReason
|
What the note tells the customer |
|---|---|---|
| The selected method is not available for this purchase |
payment_method_unsupported
|
Choose another payment method or use account funds. |
| The selected card has expired |
payment_method_expired
|
Choose another payment method or use account funds. |
| Card payments are not supported for this domain extension |
payment_method_unsupported
|
Use account funds or another payment method. |
| The payment method could not be verified (temporary) |
temporarily_unavailable
|
Try again in a few minutes, or choose another payment method. |
| Account funds do not cover the price |
insufficient_funds
|
Add funds to the account or choose a saved payment method. |
| The card was declined |
payment_declined
|
Choose another payment method, or complete the purchase on the Namecheap website. |
| The card was declined because it does not have enough funds |
insufficient_funds
|
Use another payment method or account funds, or complete the purchase on the Namecheap website. |
| The card provider rejected the card details |
payment_declined
|
Update the card on the Namecheap website or choose another payment method. |
| The card is not supported for this purchase |
payment_method_unsupported
|
Choose another payment method or use account funds. |
| The card provider could not process the charge |
temporarily_unavailable
|
Try again in a few minutes, or choose another payment method. |
| The card needs cardholder confirmation (such as 3-D Secure), which cannot be completed here |
cardholder_confirmation_required
|
Choose another payment method, or complete the purchase on the Namecheap website. |
| The card could not be charged (any charge taken is refunded automatically) |
charge_failed
|
Choose another payment method, or complete the purchase on the Namecheap website. |
A card charge that Namecheap reports as taken and not returned is not answered with payment_unavailable: it fails with infrastructure.outcomeUnknown (below).
Registration refused for a reason that is not payment. The call fails with no token, and structuredContent holds only error. Its detail starts with "The registration was not completed.", says what was refused, and ends with the support line. It does not say what to do; the table does, and so do the tool description and the usage guide:
| Cause | Code | What the detail says |
What to do |
|---|---|---|---|
| A contact field is missing (for example the registrant state or province) |
business.invalidArgument
|
The <role> contact <field> is missing. |
Update the contact, then call again. |
| A contact field is not valid |
business.invalidArgument
|
The <role> contact <field> is not valid. |
Update the contact, then call again. |
| The price changed since it was quoted |
business.invalidArgument
|
The price of this domain changed since it was quoted. | Call domain_register again without the confirmation token to get a new quote. |
| The extension or term cannot be bought through this channel |
business.invalidArgument
|
This domain extension or registration term cannot be purchased through this channel. | Offer domain_purchase_link. |
| The order could not be created |
business.conflict
|
The order could not be created. | Call again, or offer domain_purchase_link. |
Another rejection Namecheap raises before it places any order fails the same way: business.invalidArgument with the reason, and a validationErrors entry { field, rule } when it names a parameter, or business.conflict with "This domain is not available for registration." when Namecheap has just found the domain unavailable, or with "The request conflicts with the current state of the item." for any other conflict it reports. Do not repeat the purchase for either conflict.
Outcome unknown. Every other failure of the confirmed call — no answer from Namecheap, an answer that does not say whether the order went through, or an error Namecheap may raise after the order was placed or the card charged — fails with infrastructure.outcomeUnknown. The domain may have been registered and the customer charged. The detail says so and ends with the support line; structuredContent holds only error. Call domains_list before anything else, do not repeat the registration and do not offer a payment link; only if the domain is not registered there may the customer try again (see Retrying writes).
status can be:
| Status | Meaning |
|---|---|
confirmation_required
|
Preview — nothing charged. Show the response text to the user, then call again with this confirmationToken and confirmationResponse. Also returned, with a new token, when a submitted confirmationToken is missing, expired, tampered, or no longer matches the current arguments/price/payment method — never an error. |
cancelled
|
The purchase was declined (confirmationResponse: "decline"), so nothing was submitted. |
completed
|
Registered and charged. Includes charged: { amount }, an operation order receipt, a flat orderId, nextRenewal, and the purchase receipt as the response text. |
pending
|
Submitted; the registry is still finishing. The response text is a pending receipt, not a confirmation, and orderId and nextRenewal are included when known. Call domains_list shortly to confirm the final state. |
price_unavailable
|
The price could not be determined (with auto-renew on, this includes the renewal price, which must be found within 3 seconds), so no token was issued and nothing was billed. Retry later. |
payment_unavailable
|
No usable payment method exists for this purchase — no saved method can be charged and account funds don't cover the price (or the saved methods couldn't be read) — or the charge was refused for a recognised payment reason. refusalReason names the reason. The note states the actual cause when the customer can act on it — the funds balance against the price, a funds currency that cannot pay the price, a wallet with nothing chargeable, or the refused charge's reason — and stays generic only when the saved methods could not be read; paymentMethods lists the account's methods so they can choose, add a method, or add funds. |
operation here is { kind: "order", id } — an order receipt, not something to poll. The price shown at confirmation_required is exactly what will be charged on confirmationResponse: "accept" — price.amount is the total for that whole term and price.pricedYears states the term, so always show the two together. When the TLD carries an ICANN fee, price.amount already includes it and price.icannFee states the fee amount so it can be explained.
Auto-renew reporting: when autoRenew: true is requested, auto-renew is turned on right after the domain is registered and charged, as its own step. A completed registration therefore carries an additive autoRenew: { requested: true, applied }. If that separate call fails, the result still reports status: "completed" with charged set (the domain is registered and paid for) but autoRenew.applied is false, and the note explains that auto-renew did not apply and how to turn it on:
{
"domain": "example.com",
"years": 1,
"status": "completed",
"orderId": "123456",
"charged": { "amount": 9.08 },
"operation": { "kind": "order", "id": 123456 },
"nextRenewal": { "date": "2027-10-13", "price": { "amount": 10.98, "currency": "USD" }, "estimated": true },
"autoRenew": { "requested": true, "applied": false },
"price": { "amount": 9.08, "currency": "USD", "pricedYears": 1, "pricePerYear": 9.08, "icannFee": 0.2, "isPremium": false },
"note": "example.com was registered successfully. Auto-renew was requested but could not be turned on after the charge — the domain is registered and the charge has completed. Enable auto-renew again, or check its current state via domains_list."
}
autoRenew is omitted entirely when it wasn't requested (autoRenew: false on the call).
When the customer would rather check out on namecheap.com — or a payment_unavailable note asks them to complete the purchase on the website — domain_purchase_link hands them a link to pay there with a payment method they pick — see Buying through the purchase link. Never offer that link after infrastructure.outcomeUnknown without checking domains_list first.
domain_purchase_link — Get Domain Purchase LinkAccess: Write.
Hands the customer a link to buy a domain on Namecheap's own consent and payment page, instead of charging the account from the conversation. This call charges nothing and registers nothing — it only creates the purchase link.
The customer approves and pays on namecheap.com, choosing their own payment method there. Never offer it to recover from a purchase whose outcome is unknown — check domains_list first, or the customer can be charged twice. See Buying through the purchase link.
Takes domain and an optional autoRenew. The consent page has no field for term length, WHOIS privacy, or contacts, so none of those can be set through this tool.
| Parameter | Required | Type & constraints |
|---|---|---|
domain
|
Yes | String. Fully-qualified domain name the customer agreed to buy, e.g. "example.com". Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
autoRenew
|
No | Boolean. Whether the domain and its WHOIS privacy should renew automatically, charging the customer again each term. Defaults to on when omitted. The consent page shows this to the customer but does not let them change it there — they can change it afterwards in their account. |
Returns — the customer needs to open the link:
{
"domain": "example.com",
"status": "consent_required",
"consentUrl": "https://www.namecheap.com/apps/consent/1ec9218b-7766-49c8-b25f-a5cff1840746",
"expiresInSeconds": 1200,
"autoRenew": true,
"note": "To buy example.com, open this link and complete the checkout on Namecheap's site: https://www.namecheap.com/apps/consent/1ec9218b-7766-49c8-b25f-a5cff1840746\n\nAfter paying, follow the link back to finish the purchase. Nothing has been charged yet. The domain and its WHOIS privacy will renew automatically, charging the customer again each term. The consent page shows this but does not let the customer change it there — they can change it afterwards in their Namecheap account."
}
Returns — the domain cannot be bought right now:
{
"domain": "example.com",
"status": "unavailable",
"note": "example.com is not available to purchase right now. Check availability again before retrying."
}
status can be:
| Status | Meaning |
|---|---|
consent_required
|
The link was created. consentUrl is what the customer must open; expiresInSeconds is how long it stays valid. Nothing charged. |
unavailable
|
The domain is not available to purchase. This is a normal result, not an error. |
Namecheap-specific reporting: the customer approves and pays entirely on namecheap.com, and there is no follow-up call for you to make. However, the purchase is only completed once the customer follows the link back after paying — that return is what commits it. If the customer never returns, the purchase can be reverted and any amount charged is refunded automatically. So do not tell the customer the domain is theirs on the strength of this call: confirm with domains_list first. The link is single-use — reopening it after a completed purchase may report a failure even though the domain was bought, so domains_list is the authority on the outcome.
domain_set_contacts — Set Domain ContactsAccess: Sensitive write — changing the owner can lock transfers for 60 days.
Changes the contacts assigned to a domain you own. Completes immediately. You may also reuse a contactId from a contacts_list result, so look up the contacts already saved on the account and reuse one before entering new details.
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | Fully qualified domain name. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
registrant
|
Yes | Inline contact details or { contactId }. |
admin
|
No | Inline contact details, { contactId }, or null. |
tech
|
No | Inline contact details, { contactId }, or null. |
billing
|
No | Inline contact details, { contactId }, or null. |
tldAttributes
|
No | Extended attribute values keyed by name, for TLDs that require them. |
Returns
{ "verificationStatus": null }
Namecheap does not report an RAA verification state, so verificationStatus is always null.
domain_set_nameservers — Set Domain NameserversAccess: Sensitive write — moves all of the domain's traffic.
Changes a domain's registrar-level nameservers. Completes immediately. The change is reflected by domains_list (single-domain fetch) afterwards.
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | Fully qualified domain name. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically. |
provider
|
Yes |
basic (Namecheap's default nameservers) or custom (your own hosts). |
hosts
|
Conditional | Required when provider is custom: 2–12 nameserver hostnames (each a valid FQDN, 4–255 chars). Must be omitted when provider is basic. |
Returns
{ "provider": "custom", "hosts": ["ns1.example.com", "ns2.example.com"] }
Re-applying the state a domain is already in (e.g. setting basic when it is already basic) is not an error. The domain's current nameservers are read first; when they already match the request (the same provider, and for custom the same hosts in any order and letter case), nothing is written and the answer is the current state with a note:
{ "provider": "custom", "hosts": ["ns1.example.com", "ns2.example.com"], "note": "Already set; nothing changed." }
If the current nameservers cannot be read in time, or the domain uses another Namecheap DNS service (such as PremiumDNS or FreeDNS), the change is sent as requested. When Namecheap reports that it did not apply the change, the call returns business.invalidArgument ("The nameservers were not updated.").
The domainName these tools take accepts Unicode (IDN) or ASCII (A-label) and is normalized to punycode automatically; TLD support is not enforced here.
dns_records_get — Get DNS RecordsAccess: Read.
Retrieves a paginated list of DNS resource records for a domain.
A domain that uses other nameservers returns business.invalidArgument ("The domain does not use Namecheap DNS. Point its nameservers to Namecheap DNS before managing records here."); point the nameservers to Namecheap DNS first. A domain that is not in the account returns business.notFound ("This domain is not in this account.").
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | The domain whose records to fetch. |
take
|
No | Items per page, 1–500. Default 100. |
skip
|
No | Items to skip, 0 or more. Default 0. |
orderBy
|
No | Up to 8 sort keys: type, -type, name, -name. |
Returns — { items, total }, plus omittedRecords when the zone contains records this tool cannot return (see below). Each item is a record as described in Record shapes, plus an optional group field indicating where the record comes from (custom — created by you, product — managed by a product, personalNs — personal nameservers).
Namecheap-specific: a record that cannot be represented in the typed contract is omitted from items. Two kinds are omitted: record types outside the contract — the URL, FRAME and URL301 redirect records Namecheap stores in the zone (a parked domain normally has one) — and malformed CAA/TLSA/HTTPS/SVCB values. total still counts every record in the zone, including the omitted ones, so total can be larger than the number of records you can page through. Every omitted record is listed in omittedRecords by type and name, so nothing is dropped without being reported; the field is absent when nothing was omitted. Manage omitted records in the Namecheap dashboard.
{
"items": [
{ "type": "CNAME", "name": "www", "cname": "parkingpage.namecheap.com.", "ttl": 1800 }
],
"total": 2,
"omittedRecords": [
{ "type": "URL", "name": "@" }
]
}
dns_records_save — Save DNS RecordsAccess: Write.
Adds custom DNS records or updates the TTL of existing ones. Records are matched case-insensitively, except TXT records (case-sensitive).
If the call fails with infrastructure.serviceUnavailable, or reports a record as not saved because a connected service could not complete the request, the records may still have been saved: read them with dns_records_get before saving again (see Retrying writes).
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | The domain whose records to update. |
records
|
Yes | 1–500 records — see Record shapes. Each may include an optional ttl. |
force
|
No | Boolean. Skips the conflict-resolution check and forces the zone update. |
Returns — { "saved": <number> }, plus failed with force when some records were not saved (see below). Unless force is set, a successful response means every record was accepted and confirmed present in the zone; if any record fails, the whole call returns a business.conflict error instead. Its detail states how many records were saved and how many failed and names them as <type>:<name> (up to ten per list, then "and N more"), and validationErrors gives the reason for each failed record. A record named as saved was written and need not be sent again; send only the failed ones, after reading the zone with dns_records_get.
Namecheap-specific — Mail Settings and MX records: Namecheap keeps custom MX records only while the domain's Mail Settings (Advanced DNS) are set to Custom MX. In any other mode it removes the MX records from every DNS write and still reports success, so before writing the tool reads the domain's Mail Settings. When they are set to No Email Service, Email Forwarding, Private Email or Gmail and the call contains at least one MX record:
force, the call is refused before anything is written, with business.invalidArgument. The detail names the Mail Settings mode, says Namecheap does not keep custom MX records in it, and says to switch Mail Settings to Custom MX in the Namecheap dashboard (Advanced DNS) or to save the other records without the MX records. validationErrors holds one entry per submitted MX record (field MX:<name>) with the same reason. No record is saved — the other records in the call are not written either — so send them again without the MX records, or with force.force: true, the other records are written and each MX record is listed in failed as { "record": <the record as you sent it>, "reason": "<Mail Settings reason>" }. saved counts only the records written and confirmed, so saved plus the length of failed equals the number of records submitted. A call whose records are all MX answers { "saved": 0, "failed": [...] } and writes nothing.To save MX records, switch the domain's Mail Settings to Custom MX in the Namecheap dashboard (Advanced DNS) yourself; the tool never changes Mail Settings. The switch changes how the domain's mail is delivered: the Email Forwarding, Private Email or Gmail setup stops applying and mail follows only the MX records in the zone, so have the complete set of MX records ready before switching. Under Custom MX, under any other mode that keeps MX records, or when the mode cannot be determined, MX records are written and confirmed like any other record. The Mail Settings check adds no request beyond the zone read the tool already makes before writing, and no result field: the result is still { "saved": <number> } plus failed with force.
Namecheap-specific — SRV records: an SRV record is identified by its service and protocol (for example _sip._tcp) together with its priority, weight, port and target; its name is not part of the identity, because Namecheap ignores the host of an SRV record and reports it as <service>.<protocol>. Saving an SRV record with the same identity again therefore updates it (for example its TTL) instead of adding a duplicate, the re-read confirms it, and dns_records_delete finds it by the same identity whichever name you send.
Namecheap-specific — re-read after writing: after writing, the zone is re-read to confirm the records actually persisted. Namecheap can accept a record and then drop it — for example an MX record when the domain's Mail Settings changed between the Mail Settings check above and the write, or a record that conflicts with another DNS feature on the domain. A record accepted but missing from that re-read is reported as a conflict naming the record, not counted as saved. force: true opts out of that protection: the call then does not fail on a dropped record. saved counts only the records the re-read confirmed, and the others are listed in failed, each as { "record": <the record as you sent it>, "reason": "<why it did not persist>" }. failed is omitted when every record persisted. Use force only when you intend to bypass conflict handling. If the re-read itself cannot be completed, the call fails — with force set or not — with infrastructure.serviceUnavailable: the write may have been applied, so re-read the zone with dns_records_get before retrying, rather than repeating the write. Any record already known to have failed is still listed in validationErrors. SRV records are the one exception: if Namecheap cannot return SRV records during the re-read, they are reported as saved without being confirmed.
dns_records_delete — Delete DNS RecordsAccess: Sensitive write — deletions cannot be undone.
Deletes custom DNS records. Deletions cannot be undone. A submitted record is deleted only when it matches a stored record on every field; the rules are listed under the table.
| Parameter | Required | Type & constraints |
|---|---|---|
domainName
|
Yes | The domain whose records to delete. |
records
|
Yes | 1–500 records to delete — same shapes as save, but without ttl. A record that is not in the zone is reported in notFound, not as an error. |
Returns — { "deleted": <number>, "notFound": [...] }. deleted is the number of records that existed and were removed. notFound is present only when some submitted records were not in the zone; it lists them as submitted, in order, each once. Those records were not deleted and that is not an error — there is nothing to retry:
{
"deleted": 1,
"notFound": [
{ "type": "TXT", "name": "_old", "value": "v=old" }
]
}
Every submitted record that exists is deleted, even when others are missing. If Namecheap refuses to delete one of the matching records, the call fails with business.conflict and its per-record reasons; records deleted before that stay deleted and are not counted in a deleted number. The detail states how many of the matched records were deleted and how many failed and names them as <type>:<name> (up to ten per list, then "and N more"), and validationErrors gives the reason for each failed record. Submitted records that were not found are not part of that count.
How records are matched — a submitted record matches a stored one on its type, name and value, compared case-insensitively; an SRV record is matched on its service, protocol, priority, weight, port and target instead, and its name is ignored. For the value, host values (cname, aliasName, nameserver, pointer, the MX exchange, the SRV target) ignore one trailing dot and the CAA value ignores wrapping quotes; the TXT value is compared exactly, character for character. A trailing dot on name is not ignored. The MX preference is not compared: an MX record is matched by name and exchange alone, so when a zone holds several MX records for the same exchange, only one of them is matched and deleted, and it is not necessarily the one with the preference you sent. Call dns_records_get to check which MX records remain.
Every record has:
type — one of the 13 supported types below.name — the record name excluding the domain: use @ for the domain itself (apex) and * for a wildcard.ttl (save only, optional) — cache time in seconds, 60–3600.Type-specific fields:
| Type | Fields |
|---|---|
A
|
address — IPv4 address. |
AAAA
|
address — IPv6 address. |
CNAME
|
cname — canonical domain name (max 253 chars). |
ALIAS
|
aliasName — canonical domain name; CNAME-like behavior for the apex, where CNAME isn't allowed. |
NS
|
nameserver — nameserver name. |
PTR
|
pointer — domain name for the given IP address. |
TXT
|
value — text value (matched case-sensitively). |
MX
|
exchange — mail server; preference — priority (0–65535, lower preferred). |
CAA
|
flag — 0 or 128 (critical bit); tag — issue, issuewild, or iodef; value — CA identifier with optional parameters. |
SRV
|
service (e.g. _sip); protocol (e.g. _tcp); priority and weight (0–65535); port (1–65535); target — server domain name. |
TLSA
|
usage, selector, matching (each 0–255); port — * or _<1–65535>; protocol (e.g. _tcp); associationData — certificate hash or data. |
HTTPS
|
svcPriority (0–65535; 0 = AliasMode); targetName — FQDN or .; optional port (* or _<1–65535>), scheme (must be _https when port is set), svcParams. |
SVCB
|
svcPriority (0–65535; 0 = AliasMode); targetName — FQDN or .; optional port, scheme (e.g. _tcp), svcParams. |
CAA/TLSA/HTTPS/SVCB values is normalized into the fields above where possible. Values that don't fit the typed shape are readable in the zone but are omitted from dns_records_get's items and listed in its omittedRecords (see that tool). Standard records (A, AAAA, CNAME, MX, TXT, NS, etc.) are unaffected.CNAME, ALIAS, NS, PTR, the MX exchange, the SRV target) come back with a trailing dot, and a CAA value comes back quoted. You do not need to reproduce that when matching a record — dns_records_save and dns_records_delete accept either the value you originally submitted or the value dns_records_get reads back.When a call fails, the tool returns an error with a code and a human-readable detail explaining what went wrong — for example invalid input (a malformed domain name or contact ID), a domain or contact that doesn't exist, or a conflict with the current state. If a tool is not available because the assistant wasn't granted access to it, reconnect Namecheap MCP and approve the access it asks for.
A failed call always has the same shape: isError is true, the text content reads <code>: <detail>, and structuredContent.error holds code, detail and, when a rejected input has several problems, a validationErrors list. The shape is stable; read detail for the cause. Arguments that do not match a tool's parameters (a missing required field, a wrong type, a value out of range) are rejected before the tool does anything with the code application.request.badRequest: detail names each problem, and validationErrors holds one entry per problem with the argument's path in field (for example items.0.domain) and the reason in message. A call beyond the rate limit returns application.request.tooManyRequests, and a call that the token's scopes do not allow returns application.forbidden. A tool that the access you granted does not cover is not listed, and a call to it is answered as an unknown tool instead (see Access you grant).
Every other failure carries one of six codes that name the situation, so you can decide what to do next. The detail text is written by the service, is fixed for each situation and never carries internal identifiers or text from a connected system:
| Code | Meaning | Fixed detail texts |
|---|---|---|
business.notFound
|
The domain or contact is not in this account. | "This domain is not in this account."; "Contact not found in this account."; "The requested item was not found in this account." |
business.invalidArgument
|
The request was understood but the input is not acceptable. | "The domain does not use Namecheap DNS. Point its nameservers to Namecheap DNS before managing records here."; "This operation is not supported for the domain."; "Too many records for one request."; "The page size is outside the range the domains list accepts."; "The request has a missing or invalid value."; or a message naming the problem |
business.conflict
|
The request conflicts with the current state. | "The request conflicts with the current state of the item."; or a message naming the conflicting record; or, for a dns_records_save or dns_records_delete call that applied only some records, the count of records applied and failed with their names |
infrastructure.serviceUnavailable
|
Namecheap could not complete the call. Retry later. After a failed write, see Retrying writes. | "A connected service could not complete the request."; "The service could not be reached." |
infrastructure.outcomeUnknown
|
domain_register only: the purchase was sent and its outcome is unknown, so the domain may have been registered and charged. Check domains_list; do not repeat the purchase or offer a payment link. |
"The registration outcome is unknown: it may or may not have completed, and the customer may have been charged.", followed by a blank line and the support line Need help? Contact support: https://www.namecheap.com/help-center/ |
A rejection of a single value may carry one validationErrors entry shaped { "field": "<parameter name>", "rule": "<kind of rejection>" }, for example { "field": "DOMAINNAME", "rule": "NOT_AVAILABLE_OR_NOT_FOUND" }; the texts above are fixed and name no value you sent.
A failure the service cannot attribute to a situation above, such as an unexpected internal fault, returns the code infrastructure.unhandled with the fixed detail "The request could not be completed because of an internal error." Retry such a call later; repeating it at once rarely helps.
A write that fails with infrastructure.serviceUnavailable may still have been applied. Before calling it again:
contacts_save — look for the contact with contacts_list first.dns_records_save — read the records with dns_records_get first.domain_set_contacts — call it again; it assigns the same contacts again.domain_set_nameservers — call it again. A repeat that finds the nameservers already set answers with the note "Already set; nothing changed."; domains_list shows the current nameservers, so read it when an answer leaves you unsure.dns_records_delete — call it again; a record the first call already removed comes back in notFound, or in unconfirmed when the zone could not be read fully.domain_register — check domains_list for the domain before submitting the purchase again. After infrastructure.outcomeUnknown the domain may have been registered and charged: do not submit the purchase again or offer a payment link unless domains_list shows it is not registered.Tools that only read can be called again at any time. A call refused with
application.request.tooManyRequests was not run: wait a short while before calling it again.
Need help? We're always here for you.