Spaceship MCP — Tools Reference

Spaceship MCP connects your AI assistant (such as Claude) to your Spaceship account. Through it, the assistant can check and register domains, get payment links to buy domains in your browser, 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.

Getting started

You need a Spaceship account. Spaceship MCP is available at https://mcp.spaceship.com/mcp.

How you connect depends on your AI assistant:

  • Claude (web and desktop) — open Settings, choose Connectors, add a custom connector, and point it at https://mcp.spaceship.com/mcp. Anthropic's Claude is currently the client we have verified Spaceship MCP to work with.

  • Other MCP clients — add a remote MCP server and point it at https://mcp.spaceship.com/mcp. Other clients may work, but we haven't verified them yet.

When you connect, you'll be asked to sign in to Spaceship and grant the assistant access to your account. Which tools the assistant can use depends on the access you approve — if a tool is rejected because access wasn't granted, reconnect and approve the access it needs.

Terms and privacy

The agreements and policies that apply to Spaceship MCP and to anything you buy through it:

Tools at a glance

  • Tool: contacts_save

    What it does: Save contact details and get a contact ID

  • Tool: contacts_get

    What it does: Read a saved contact by its ID

  • Tool: contacts_list

    What it does: List all saved contacts to find and reuse one

  • Tool: domains_list

    What it does: List your domains, or look up one domain

  • Tool: domains_check_availability

    What it does: Check if domains are available to register

  • Tool: domain_register

    What it does: Register (buy) a domain — spends money

  • Tool: domain_purchase_link

    What it does: Get payment links to buy domains on Spaceship's payment page — nothing is charged by the call

  • Tool: domain_set_contacts

    What it does: Assign contacts to a domain you own

  • Tool: domain_set_nameservers

    What it does: Switch a domain to basic or custom nameservers

  • Tool: dns_records_get

    What it does: Read DNS records for a domain

  • Tool: dns_records_save

    What it does: Add DNS records or update their TTL

  • Tool: dns_records_delete

    What it does: Delete DNS records

  • Tool: async_operation_get

    What it does: Check the status of a long-running operation

Contacts: referenced by id

Wherever a contact is required (domain_register, domain_purchase_link, domain_set_contacts), each role takes a contactId string — never inline contact details. Save the contact first with contacts_save (which returns its contactId), then pass that id where the contact is accepted. There is no inline auto-save; a role cannot receive a full contact object. You may also reuse a contactId from a contacts_list result or one you read off a domains_list result.

A contactId is a string of 27–32 alphanumeric characters. Just pass it back where a contact is accepted.

Common workflows

Several tools are designed to be used together: the output of one becomes the input of the next.

Register (buy) a domain

  1. contacts_save — save the registrant, admin, tech, and billing contacts (if you don't already hold their ids) and keep the returned contactId for each. Contacts must exist before you can register.

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

  3. 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 (or funds when there's 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 — term, price breakdown, auto-renew, WHOIS privacy, payment source, and the registrant/admin/tech/billing contacts — show it to the user as-is. Pick years between minRegisterPeriodInYears and maxRegisterPeriodInYears from step 2 — an out-of-range value is rejected outright. Pass each contact role as the contactId you saved in step 1.

  4. 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 immediately with status: pending and an operationId — registration finishes in the background. To cancel instead, call again with the same confirmationToken and confirmationResponse: "decline" — nothing is billed. The token expires after a short time and is bound to the exact arguments, price, and payment method it was issued for; if it's missing, expired, or no longer matches, the call returns a brand-new confirmation instead of an error — never a charge. If the price can't be determined on either call, 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.

  5. async_operation_get — pass the operationId from step 4 to check progress. Repeat until status becomes success or failed.

contacts_save ──▶ domains_check_availability ──▶ domain_register ──▶ domain_register ──▶ async_operation_get
(contactId ids) (available? + price + (token unset: (token + (pending →
min/maxRegisterPeriod) confirmation, accept: operationId, success/failed)
confirmationToken, pending)
no charge)

Use this instead of domain_register when you ask for a payment link, when buying directly in the chat failed, or when you want to pay with a payment method the chat purchase can't use.

  1. domains_check_availability — check the name(s) you want. Only proceed when result is available, and read minRegisterPeriodInYears/maxRegisterPeriodInYears to pick a valid years.

  2. domain_purchase_link — pass the domains as items and the contacts to put on them as contacts — contact IDs from contacts_list, or save new details with contacts_save first — with shared years and autoRenew at the top level and per-domain overrides where they differ. This call charges nothing. It returns one link per group of up to 10 domains, with an estimated price for each; you open each link and pay on Spaceship's payment page, where you choose the payment method (account funds are pre-selected when you have them). WHOIS privacy can't be set this way.

  3. domains_list — after you've paid, call it for the domain to confirm it is registered. Don't treat the link as proof of purchase.

Don't ask for a second link for a purchase that may already have gone through. If a domain_register attempt may have billed, or you followed an earlier link, call domains_list first and ask for a new link only for domains that are genuinely not registered. Links don't expire and can be reused, so don't share them with anyone else.

domains_check_availability ──▶ domain_purchase_link ──▶ (domains_list to confirm)
(available? + price + (links, up to 10 domains after you've paid
min/maxRegisterPeriod) each; no charge from
this call)

Update the contacts on a domain you own

  1. domain_set_contacts — assign contacts to the domain by contactId (save them with contacts_save first if needed). This completes immediately and returns a verificationStatus: verification means the registrant must confirm their email address before the change fully applies (an email is sent to them), success means it's already confirmed, and null means no confirmation is required for that domain.

Change a domain's nameservers

  1. domains_list — find the domain and see its current nameservers ({ provider, hosts }).

  2. domain_set_nameservers — switch it to Spaceship'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 }, and a subsequent domains_list reflects the change. Re-applying the state a domain is already in returns a validation error rather than a no-op — treat that as expected, not a failure to retry.

Manage DNS records

  1. domains_list — find the domain you want to manage (or pass its name directly if you know it).

  2. dns_records_get — read the current records for the domain.

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

Review your portfolio

  • domains_list — page through all your domains with sorting, or fetch a single domain by name. Each domain includes its expiration date, auto-renew setting, status, nameservers, privacy protection, and assigned contact IDs.

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

Tool reference

Every tool returns its result as structured JSON. Long-running operations (currently only domain_register) return an operation reference to poll with async_operation_get; all other tools complete immediately.

Contacts

Contacts are the people or organisations attached to a domain registration (registrant, admin, tech, billing). A contact is referenced everywhere by its contact ID — an opaque string.

contacts_save — Save Contact

Saves contact details and returns the generated contact ID. Validation of some fields (such as stateProvince and postalCode) depends on the selected country.

  • Parameter: firstName

    Required: Yes

    Type & constraints: String, 1–64 chars. May include hyphens and apostrophes.

  • Parameter: lastName

    Required: Yes

    Type & constraints: String, 1–64 chars. May include hyphens and apostrophes.

  • Parameter: email

    Required: Yes

    Type & constraints: Valid email address, max 254 chars.

  • Parameter: address1

    Required: Yes

    Type & constraints: Address line 1. String, 1–128 chars.

  • Parameter: city

    Required: Yes

    Type & constraints: String, 1–64 chars.

  • Parameter: country

    Required: Yes

    Type & constraints: Two-letter country code (ISO 3166-1 alpha-2), e.g. US.

  • Parameter: phone

    Required: Yes

    Type & constraints: International format +CountryCode.Number, e.g. +1.2025551234. Max 32 chars.

  • Parameter: organization

    Required: No

    Type & constraints: Organization/company name. 1–128 chars.

  • Parameter: address2

    Required: No

    Type & constraints: Address line 2. 1–128 chars.

  • Parameter: stateProvince

    Required: No

    Type & constraints: State/province name, 1–64 chars. May be required depending on the country.

  • Parameter: postalCode

    Required: No

    Type & constraints: 1–16 chars. May be required depending on the country.

  • Parameter: phoneExt

    Required: No

    Type & constraints: Phone extension, 1–16 chars.

  • Parameter: fax

    Required: No

    Type & constraints: Fax number, same +CountryCode.Number format, max 32 chars.

  • Parameter: faxExt

    Required: No

    Type & constraints: Fax extension, 1–16 chars.

  • Parameter: taxNumber

    Required: No

    Type & constraints: Tax number, 1–32 chars.

Returns

{ "contactId": "..." }

contactId (27–32 alphanumeric characters) is what you pass to domain_register, domain_purchase_link, domain_set_contacts, and contacts_get.

contacts_get — Get Contact

Reads the details of a saved contact by its contact ID. Contact IDs come from contacts_save, contacts_list, or the contacts field of domains_list results.

  • Parameter: contactId

    Required: Yes

    Type & constraints: Contact ID, 27–32 alphanumeric characters.

Returns — { contact } with:

  • Field: firstName, lastName, email, address1, city, country, phone, postalCode

    Type: String

  • Field: organization, address2, stateProvince, phoneExt, fax, faxExt, taxNumber

    Type: String or null

contacts_list — List Contacts

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 or hunting through your domains. The list is paginated and sortable, consistent with domains_list.

  • Parameter: take

    Required: No

    Type & constraints: Items per page, 1–100. Default 10.

  • Parameter: skip

    Required: No

    Type & constraints: Items to skip, 0 or more. Default 0.

  • Parameter: orderBy

    Required: No

    Type & constraints: Up to 8 sort keys: name, email, organization; prefix with - for descending (e.g. -name).

Returns — { items, total } where total is the number of unique contacts on the account (deduplicated by contact ID, not the page size), and each item carries enough to tell contacts apart without a follow-up call. If the account has duplicate entries for the same contact ID they are collapsed to one, so total counts distinct contacts rather than raw server-side rows:

  • Field: contactId

    Type: String (27–32 alphanumeric). Pass to contacts_get, domain_register, domain_purchase_link, or domain_set_contacts.

  • Field: name

    Type: String — the contact's name.

  • Field: email

    Type: String or null when the contact has no email on record.

  • Field: organization

    Type: String or null when the contact has no organization on record.

{
"items": [
{ "contactId": "1anq5bsl9haBy21rOV9aeDWrARBsV", "name": "Ada Lovelace", "email": "ada@example.com", "organization": "Analytical Engines" }
],
"total": 1
}

Domains

Domain name inputs (domain/domainName) accept Unicode (IDN) or ASCII (A-label) — either way, the tool normalises the name to punycode automatically before use. domains_check_availability and domain_register additionally require a TLD Spaceship 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_set_contacts, domain_set_nameservers) and the DNS tools normalise the name only and never reject on TLD support.

domains_list — List Domains

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

  • Parameter: domain

    Required: No

    Type & constraints: Fully qualified domain name to fetch a single domain. Accepts Unicode (IDN) or ASCII (A-label) — normalised to punycode automatically.

  • Parameter: take

    Required: No

    Type & constraints: Items per page, 1–100. Default 10.

  • Parameter: skip

    Required: No

    Type & constraints: Items to skip, 0 or more. Default 0.

  • Parameter: orderBy

    Required: No

    Type & constraints: Up to 8 sort keys: name, unicodeName, registrationDate, expirationDate; prefix with - for descending (e.g. -expirationDate).

Returns — { items, total } where each item describes a domain:

  • Field: name / unicodeName

    Meaning: Domain name in ASCII and Unicode form.

  • Field: isPremium

    Meaning: Whether the domain is a premium name.

  • Field: autoRenew

    Meaning: Whether auto-renew is enabled.

  • Field: registrationDate / expirationDate

    Meaning: Registration and expiration timestamps.

  • Field: lifecycleStatus

    Meaning: creating, registered, grace1, grace2, or redemption.

  • Field: verificationStatus

    Meaning: verification, success, failed, or null when not applicable.

  • Field: eppStatuses

    Meaning: Registry status codes (e.g. transfer locks).

  • Field: suspensions

    Meaning: Active suspensions, each with a reasonCode.

  • Field: privacyProtection

    Meaning: { level: "public" | "high", contactForm: boolean }.

  • Field: nameservers

    Meaning: { provider: "basic" | "custom", hosts: [...] }.

  • Field: contacts

    Meaning: Contact IDs: registrant, plus admin/tech/billing (may be null) and attributes (a list of extended-attribute contact IDs, or null). Readable via contacts_get.

Spaceship MCP populates every field above — including contacts, eppStatuses, suspensions, verificationStatus, nameservers, a real autoRenew, and a distinct unicodeName where the domain has one — for both the multi-item list and single-domain fetches.

domains_check_availability — Check Domain Availability

Checks whether one or more domain names are available to register. Uses the single-domain endpoint for one name and the bulk endpoint for multiple. 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: domains

    Required: Yes

    Type & constraints: 1–20 fully qualified domain names. Each accepts Unicode (IDN) or ASCII (A-label) — normalised to punycode automatically.

Returns — { results }, one entry per requested name:

  • Field: domain

    Meaning: The checked name.

  • Field: result

    Meaning: available, taken, invalidDomainName, tldNotSupported, or unexpectedError.

  • Field: premiumPricing

    Meaning: For premium names: list of { operation, price, currency } where operation is register, transfer, renew, or restore. Empty for regular names.

  • Field: price

    Meaning: 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.

  • Field: pricePerYear

    Meaning: 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.

  • Field: minRegisterPeriodInYears / maxRegisterPeriodInYears

    Meaning: 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.

  • Field: priceUnavailableReason

    Meaning: 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.

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": 159.96, "currency": "USD", "pricedYears": 2, "pricePerYear": 79.98, "isPremium": false },
"minRegisterPeriodInYears": 2,
"maxRegisterPeriodInYears": 10
}

pricePerYear is present here — 159.96 divided by the two years it covers gives 79.98. 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 ("$159.96 for 2 years"), never amount alone. For an ordinary TLD, pricedYears is 1 and pricePerYear equals amount.

domain_register — Register Domain

Registers (buys) a domain. This charges a payment method — your default saved method, account funds, or one you choose — and is irreversible. 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.

Two-step pre-charge confirmation. Call first with confirmationToken unset: the tool prices the domain fresh, resolves the payment method, builds a full confirmation — term, price breakdown (including any ICANN fee and whether the domain is premium), auto-renew, WHOIS privacy, payment source, and the registrant/admin/tech/billing contacts (a contact identical to the registrant is shown as "same as registrant") — 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. 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, the tool returns status: "price_unavailable" instead of a token and never charges; retry later. A confirmed call returns immediately with status: "pending" and an operationId — the registration finishes in the background; check it with async_operation_get.

Choosing a payment method. Omit paymentMethodId to charge the account's default saved payment method, or account funds when there is no usable default — the confirmation names the resolved source in paymentSource and its response text. When it's omitted, the response also carries paymentMethods: the account's saved methods, each with an id and a display label (e.g. "Card ···2584" 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. 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.

  • Parameter: domain

    Required: Yes

    Type & constraints: Fully qualified domain name to register, e.g. example.com. Accepts Unicode (IDN) or ASCII (A-label) — normalised to punycode automatically.

  • Parameter: years

    Required: Yes

    Type & constraints: 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.

  • Parameter: autoRenew

    Required: Yes

    Type & constraints: Boolean. When true, the domain renews automatically at expiration using the account's default payment method.

  • Parameter: privacy.level

    Required: Yes

    Type & constraints: high hides the registrant's contact details from public WHOIS; public publishes them.

  • Parameter: privacy.userConsent

    Required: Yes

    Type & constraints: Boolean. Must confirm you agree to the selected privacy setting.

  • Parameter: contacts.registrant

    Required: Yes

    Type & constraints: contactId string (27–32 alphanumeric), from contacts_save.

  • Parameter: contacts.admin

    Required: Yes

    Type & constraints: contactId string (27–32 alphanumeric), from contacts_save.

  • Parameter: contacts.tech

    Required: Yes

    Type & constraints: contactId string (27–32 alphanumeric), from contacts_save.

  • Parameter: contacts.billing

    Required: Yes

    Type & constraints: contactId string (27–32 alphanumeric), from contacts_save.

  • Parameter: contacts.attributes

    Required: No

    Type & constraints: Array of extended-attribute contact ids (up to 5); required only for certain TLDs, omit or null otherwise.

  • Parameter: paymentMethodId

    Required: No

    Type & constraints: 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 (or funds when there is no usable default).

  • Parameter: confirmationToken

    Required: No

    Type & constraints: 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.

  • Parameter: confirmationResponse

    Required: Conditional

    Type & constraints: "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), when the customer has a usable default and doesn't need to choose:

{
"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": "Card ···2584",
"note": "Nothing has been charged yet. Show the confirmation to the user and, once they agree, call domain_register again with this confirmationToken and confirmationResponse=\"accept\" to complete the purchase, or confirmationResponse=\"decline\" to cancel."
}

Returns — after the first call with paymentMethodId omitted, additionally carrying the account's saved 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": "Account funds (USD 17.29)",
"paymentMethods": [
{ "id": "pm_9f2c1a7e", "type": "Funds", "order": 1, "label": "Account funds (USD 17.29)" },
{ "id": "pm_4b6e2d90", "type": "CreditCard", "order": 2, "label": "MasterCard ···2584" }
],
"note": "Nothing has been charged yet. Show the confirmation to the user 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. 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": 48.52, "pricedYears": 5, "pricePerYear": 9.70 }, and example.ai with years: 2 returns { "amount": 159.96, "pricedYears": 2, "pricePerYear": 79.98 }.

A paymentMethods entry never carries card numbers, cardholder names, or any billing/issuer detail beyond label — isExpired/offSessionForbidden (when true) flag a method as not currently usable, and availableBalance/balanceCurrency are present only for the Funds entry.

Returns — after confirmationResponse: "accept" (registration submitted):

{
"domain": "example.com",
"years": 1,
"status": "pending",
"operationId": "...",
"price": {
"amount": 9.08,
"currency": "USD",
"pricedYears": 1,
"pricePerYear": 9.08,
"icannFee": 0.2,
"isPremium": false
},
"note": "Registration of example.com submitted. Ask again, or call async_operation_get with this operationId, to check status."
}

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": "pm_4b6e2d90", "type": "CreditCard", "order": 2, "label": "MasterCard ···2584", "isExpired": true }
],
"note": "example.com was not registered and nothing was charged. None of the saved payment methods can be charged for this purchase and the account funds cannot be used. Add a payment method or add funds to the account, then try again."
}

status can be:

  • Status: confirmation_required

    Meaning: 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.

  • Status: cancelled

    Meaning: The purchase was declined (confirmationResponse: "decline"), so nothing was submitted.

  • Status: pending

    Meaning: Submitted; the registry is finishing in the background. Poll async_operation_get with the operationId.

  • Status: price_unavailable

    Meaning: The price could not be determined, so no token was issued and nothing was billed. Retry later.

  • Status: payment_unavailable

    Meaning: 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). No token was issued and nothing was billed. 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, or a wallet with nothing chargeable — and stays generic only when the saved methods could not be read; paymentMethods lists the account's saved methods so the customer can choose, add a method, or add funds.

operationId is a flat string — pass it to async_operation_get, which reports whether the registration ultimately succeeds or fails. 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.

Prepares one or more Spaceship payment links so you can buy domains on Spaceship's own payment page in your browser instead of in the chat. Nothing is registered or charged by this call — you review and pay on the page, which is the confirmation step. Use it when you ask for a payment link, when buying directly in the chat failed, or when you want to pay with a payment method the chat purchase can't use. If a purchase attempt may already have gone through, check domains_list first and ask for a link only for domains that are genuinely not registered, or you risk paying twice. Requires the domains:billing access.

Settings given at the top level (years, autoRenew, contacts) apply to every item. An item that sets its own years, autoRenew or contacts overrides them for that domain only, and anything it leaves out is inherited (an item's contacts replace the top-level contacts as a whole). The top-level contacts is required: the contacts travel with the link and are already filled in as the domain's contacts when you open it. Each domain may be listed only once, and there are no payment, currency or WHOIS privacy inputs — WHOIS privacy stays at the platform default on the payment page.

  • Parameter: items

    Required: Yes

    Type & constraints: Array of 1–20 items, each listed once.

  • Parameter: items[].domain

    Required: Yes

    Type & constraints: Fully qualified domain name to buy, e.g. example.com. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically.

  • Parameter: items[].years

    Required: No

    Type & constraints: Integer 1–10; overrides the top-level years for this domain.

  • Parameter: items[].autoRenew

    Required: No

    Type & constraints: Boolean; overrides the top-level autoRenew for this domain.

  • Parameter: items[].contacts

    Required: No

    Type & constraints: Contacts for this domain only, as contactId strings (registrant, admin, tech, billing, optional attributes); replace the top-level contacts as a whole.

  • Parameter: years

    Required: No

    Type & constraints: Integer 1–10, applied to every item that doesn't set its own. Must fall inside the term the TLD allows (minRegisterPeriodInYears/maxRegisterPeriodInYears from domains_check_availability); out-of-range values are rejected with an error naming the allowed range, not adjusted. When neither this nor the item sets it, the TLD's shortest term is used.

  • Parameter: autoRenew

    Required: No

    Type & constraints: Boolean, applied to every item that doesn't set its own. Defaults to true.

  • Parameter: contacts

    Required: Yes

    Type & constraints: Contacts as contactId strings, applied to every item that doesn't set its own: registrant (required), admin, tech and billing (each defaults to registrant), and optional attributes (a list of up to 5 extended-attribute contact IDs, required only for certain TLDs). Take the IDs from contacts_list, or save the details with contacts_save first.

Links per call. A single link holds at most 10 domains, so a longer list returns several links in request order — for example 15 domains produce two links of 10 and 5. links[].domains says which domains each link covers.

Price. Each domain carries an estimatedprice (the final price is shown on the payment page) or a priceUnavailableReason when it can't be determined. A missing estimate never stops a link from being issued.

Payment. The payment method is chosen on the page. Account funds are pre-selected when the account has them.

Links are reusable. A link does not expire and can be opened more than once, so don't share it with anyone else.

Returns

{
"status": "links_ready",
"links": [
{
"url": "https://example.spaceship.com/pay/abc123",
"domains": [
{
"domain": "example.com",
"years": 1,
"autoRenew": true,
"price": { "amount": 9.08, "currency": "USD", "pricedYears": 1, "pricePerYear": 9.08, "icannFee": 0.2, "isPremium": false }
},
{
"domain": "example.ai",
"years": 2,
"autoRenew": false,
"priceUnavailableReason": "Price is currently unavailable for this domain."
}
]
}
],
"failed": [],
"note": "Nothing has been charged. ..."
}

  • status: links_ready

    Meaning: Every domain is covered by a link.

  • status: partial

    Meaning: Some domains are covered; the rest are listed in failed, each with a reason (for example not available, unsupported extension, term not allowed, contacts problem, or a link that couldn't be created).

  • status: no_links

    Meaning: No link could be prepared. The result is flagged as an error, failed says why, and nothing was charged.

After the customer says they have paid, call domains_list for the domain to confirm it is registered — don't treat the link alone as proof of purchase.

domain_set_contacts — Set Domain Contacts

Changes the contacts assigned to a domain you own. Completes immediately (no operation to poll).

  • Parameter: domainName

    Required: Yes

    Type & constraints: Fully qualified domain name. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically.

  • Parameter: registrant

    Required: Yes

    Type & constraints: contactId string (27–32 alphanumeric), from contacts_save.

  • Parameter: admin

    Required: No

    Type & constraints: contactId string (27–32 alphanumeric) or null.

  • Parameter: tech

    Required: No

    Type & constraints: contactId string (27–32 alphanumeric) or null.

  • Parameter: billing

    Required: No

    Type & constraints: contactId string (27–32 alphanumeric) or null.

  • Parameter: attributes

    Required: No

    Type & constraints: Array of extended-attribute contact ids (up to 5); required only for certain TLDs, omit or null otherwise.

Returns

{ "verificationStatus": "verification" }

The returned verificationStatus reflects ICANN RAA email verification: verification — the registrant must confirm their email address (a confirmation email is sent); success — already confirmed; null — RAA verification does not apply to this domain.

domain_set_nameservers — Set Domain Nameservers

Changes a domain's registrar-level nameservers. Completes immediately (no operation to poll). The change is reflected by domains_list afterwards.

  • Parameter: domainName

    Required: Yes

    Type & constraints: Fully qualified domain name. Accepts Unicode (IDN) or ASCII (A-label) — normalized to punycode automatically.

  • Parameter: provider

    Required: Yes

    Type & constraints: basic (Spaceship's default nameservers) or custom (your own hosts).

  • Parameter: hosts

    Required: Conditional

    Type & constraints: 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) returns a validation error rather than a no-op success — treat that as an expected result, not a failure to retry.

DNS records

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 Records

Retrieves a paginated list of DNS resource records for a domain.

  • Parameter: domainName

    Required: Yes

    Type & constraints: The domain whose records to fetch.

  • Parameter: take

    Required: No

    Type & constraints: Items per page, 1–500. Default 100.

  • Parameter: skip

    Required: No

    Type & constraints: Items to skip, 0 or more. Default 0.

  • Parameter: orderBy

    Required: No

    Type & constraints: Up to 8 sort keys: type, -type, name, -name.

Returns — { items, total }. 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 Spaceship product, personalNs — personal nameservers).

dns_records_save — Save DNS Records

Adds custom DNS records or updates the TTL of existing ones. Records are matched case-insensitively, except TXT records (case-sensitive).

  • Parameter: domainName

    Required: Yes

    Type & constraints: The domain whose records to update.

  • Parameter: records

    Required: Yes

    Type & constraints: 1–500 records — see Record shapes. Each may include an optional ttl.

  • Parameter: force

    Required: No

    Type & constraints: Boolean. Skips the conflict-resolution check and forces the zone update.

Returns — { "saved": <number> }, the count of submitted records. A successful response means all records were accepted; if any record fails, the whole call returns an error instead.

dns_records_delete — Delete DNS Records

Deletes custom DNS records. Deletions cannot be undone. Records are matched case-insensitively, except TXT records (case-sensitive).

  • Parameter: domainName

    Required: Yes

    Type & constraints: The domain whose records to delete.

  • Parameter: records

    Required: Yes

    Type & constraints: 1–500 records identifying existing records — same shapes as save, but without ttl.

Returns — { "deleted": <number> }, the count of submitted records. If any record cannot be matched, the whole call fails and nothing is deleted.

Record shapes

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: A

    Fields: address — IPv4 address.

  • Type: AAAA

    Fields: address — IPv6 address.

  • Type: CNAME

    Fields: cname — canonical domain name (max 253 chars).

  • Type: ALIAS

    Fields: aliasName — canonical domain name; CNAME-like behaviour for the apex, where CNAME isn't allowed.

  • Type: NS

    Fields: nameserver — nameserver name.

  • Type: PTR

    Fields: pointer — domain name for the given IP address.

  • Type: TXT

    Fields: value — text value (matched case-sensitively).

  • Type: MX

    Fields: exchange — mail server; preference — priority (0–65535, lower preferred).

  • Type: CAA

    Fields: flag — 0 or 128 (critical bit); tag — issue, issuewild, or iodef; value — CA identifier with optional parameters.

  • Type: SRV

    Fields: service (e.g. _sip); protocol (e.g. _tcp); priority and weight (0–65535); port (1–65535); target — server domain name.

  • Type: TLSA

    Fields: usage, selector, matching (each 0–255); port — * or _<1–65535>; protocol (e.g. _tcp); associationData — certificate hash or data.

  • Type: HTTPS

    Fields: svcPriority (0–65535; 0 = AliasMode); targetName — FQDN or .; optional port (* or _<1–65535>), scheme (must be _https when port is set), svcParams.

  • Type: SVCB

    Fields: svcPriority (0–65535; 0 = AliasMode); targetName — FQDN or .; optional port, scheme (e.g. _tcp), svcParams.

Async operations

async_operation_get — Get Async Operation Status

Checks a long-running operation started by another tool (currently domain_register). Call it with operationId set to the operationId that tool returned, and repeat until status is success or failed.

  • Parameter: operationId

    Required: Yes

    Type & constraints: Alphanumeric string, max 36 chars, returned by the tool that started the operation.

Returns

  • Field: operationId

    Meaning: The polled operation.

  • Field: status

    Meaning: pending, success, or failed.

  • Field: type

    Meaning: Operation type, or null.

  • Field: details

    Meaning: Extra details about the operation, or null.

  • Field: createdAt / modifiedAt

    Meaning: When the operation was created / last updated (modifiedAt may be null).

Errors

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 rejected because the assistant wasn't granted access to it, reconnect Spaceship MCP and approve the access it asks for.

A valid email is required