The command-line interface for name.com
Register domains, manage DNS, and run your entire domain portfolio — without leaving the terminal.
Recorded against the sandbox API with vhs — see docs/demo.tape for the script.
And namecom status, for the portfolio at a glance — illustrated below with a
representative account rather than a real one:
$ namecom status
Profile default https://api.name.com
47 domains 3 expiring within 30 days 1 transfer pending 2 unlocked
Balance $125.40
Expiring soon
acme.io 2026-10-18 (16 days)
staging.dev 2026-10-25 (23 days)
oldsite.net 2026-10-30 (28 days)
Transfers in progress
newco.com
→ Run 'namecom domain renew <domain>' to renew expiring domains
→ Run 'namecom domain list' to see all domains
namecom status -q prints just the expired and soon-expiring domains, one per
line, ready for xargs.
- Why
- Installation
- Quick start
- Commands
- Workflows
- Output formats
- JSON contract
- Configuration
- Shell completion
- Global flags
- Exit codes
- Idempotency keys
- Development
- Contributing
- Changelog
- Security policy
- License
The name.com web UI is great for humans. The CLI is for agents and terminal wizards
- Automate domain renewals, DNS changes, and email forwards in CI/CD pipelines
- Script bulk operations across dozens of domains at once
- Integrate with secret managers via
token_cmd— no credentials in shell history - Pipe JSON output directly into
jq,grep, and other tools - Stay fast — tab completion,
--dry-run, and--yesflags for confident automation
Homebrew (macOS / Linux):
brew tap patramsey/tap
brew trust patramsey/tap
brew install namecomUpgrading from v0.2.4 - v0.3.1? Those versions shipped as a Homebrew cask; this is a formula again.
brew updatemigrates you automatically but cannot finish the job, because Homebrew will not link a formula while a cask of the same name is installed. Two steps remain:brew uninstall --cask --force namecom brew link namecomWithout the second,
namecomwill not be on your PATH. Fresh installs need neither.
Download a release binary:
# macOS (Apple Silicon)
curl -L https://github.com/patramsey/namecom-cli/releases/latest/download/namecom_darwin_arm64.tar.gz | tar xz
sudo mv namecom /usr/local/bin/
# macOS (Intel)
curl -L https://github.com/patramsey/namecom-cli/releases/latest/download/namecom_darwin_amd64.tar.gz | tar xz
sudo mv namecom /usr/local/bin/
# Linux (amd64)
curl -L https://github.com/patramsey/namecom-cli/releases/latest/download/namecom_linux_amd64.tar.gz | tar xz
sudo mv namecom /usr/local/bin/All platforms and checksums on the releases page.
macOS: these binaries are not signed with an Apple Developer ID. The commands above are unaffected —
curldoes not set the quarantine attribute — but a tarball downloaded through a browser is quarantined, and macOS will refuse to run it with "Apple could not verify 'namecom' is free of malware." Clear it withxattr -d com.apple.quarantine namecom, or approve the binary under System Settings → Privacy & Security. Homebrew installs are unaffected.
Go install:
go install github.com/patramsey/namecom-cli@latest
mv "$(go env GOPATH)/bin/namecom-cli" "$(go env GOPATH)/bin/namecom"go install names the binary after the module path (namecom-cli); the mv renames it to namecom to match the rest of this README.
# 1. Authenticate (create an API token at https://www.name.com/account/settings/api)
namecom auth login
# 2. See your portfolio at a glance
namecom status
# 3. Check if a domain is available
namecom domain check mycoolstartup.com
# 4. Register it
namecom domain register mycoolstartup.com
# 5. Point it somewhere
namecom dns create mycoolstartup.com --type A --answer 1.2.3.4
# Tip: jump to the name.com dashboard for any domain
namecom open mycoolstartup.com| Group | Commands |
|---|---|
domain |
list get search check register renew lock autorenew privacy set-ns contacts auth-code pricing update claims requirements |
dns |
list create update delete export import sync |
dnssec |
list get create delete |
transfer |
list get create cancel eligibility internal-in cancel-outbound |
email |
list get create update delete |
url |
list get create update delete |
vanity-ns |
list get create update delete |
contact |
unverified resend verify — ICANN contact verification |
auth |
login logout status |
status |
account overview: domain counts, expiring domains, pending transfers, balance |
order |
list get refund |
config |
list-profiles (alias profiles) use show |
api |
raw HTTP passthrough with auth applied |
open |
open name.com in a browser (honors $BROWSER; prints the URL when no browser can be opened) |
version |
version and build information |
completion |
shell completion scripts: bash zsh fish powershell |
Every list also answers to ls and every delete to rm. In dns,
dnssec, email, url and vanity-ns, create also answers to add.
namecom --help
namecom domain --help
namecom dns --help
Register a domain and set it up:
namecom domain check acme.io # check availability
namecom domain register acme.io # register it
namecom dns create acme.io --type A --answer 1.2.3.4
namecom dns create acme.io --type MX --answer mail.google.com --priority 10
namecom email create acme.io hello --to you@gmail.com # hello@acme.io → you@gmail.com
namecom domain autorenew on acme.io # never let it expireManage DNS records:
namecom dns list acme.io
namecom dns create acme.io --type CNAME --host www --answer acme.io.
namecom dns update acme.io 12345 --answer 5.6.7.8
namecom dns export acme.io --zone > acme.io.zone # export as BIND zone file
namecom dns list acme.io --host www # only the records at wwwKeep DNS in a file and sync it (re-runnable: a second run changes nothing):
namecom dns export acme.io --zone > acme.io.zone # 1. snapshot the live zone
$EDITOR acme.io.zone # 2. edit it, or keep it in git
namecom dns sync acme.io --file acme.io.zone --dry-run # 3. see the plan; nothing is sent
namecom dns sync acme.io --file acme.io.zone # 4. apply it, after a confirmation
namecom dns sync acme.io --file acme.io.zone --prune # ...also deleting what the file droppeddns sync matches records on host, type and answer: a TTL or priority change
is an update, and a changed answer is a new record (the old one is deleted
only with --prune; a CNAME's target is updated in place, since a name holds
one CNAME). Without --prune nothing is deleted. --prune never touches NS
records at the apex — the domain's delegation — or CAA records, which the API
cannot recreate; --prune-all does. A file with no records is refused with
either, so a wrong path cannot empty the zone. Changes are applied creates first, then
updates, then deletes. If one fails, sync stops, reports what was applied, and
exits non-zero; fix the cause and run it again. The file can also be the JSON
dns export writes. In CI, --dry-run -o json gives the plan as one
document, and --yes skips the confirmation.
For scripts that add or remove single records, dns create --if-not-exists,
dns delete --if-exists and dns import --skip-existing succeed when the
work is already done, so a retry does not fail.
Transfer a domain in:
namecom transfer eligibility acme.io # confirm it's eligible
namecom transfer create acme.io --auth-code XXXXXX
namecom transfer get acme.io # check status
# set WHOIS contacts on arrival (same JSON as domain register --contacts-file);
# changing contacts may start a registrar transfer lock
namecom transfer create acme.io --auth-code XXXXXX --contacts-file contacts.jsonSet up email forwarding and URL forwarding:
namecom email create acme.io hello --to you@gmail.com # hello@acme.io → you@gmail.com
namecom email list acme.io
namecom url create acme.io --to https://new-site.com # redirect apex to another URLPublish DNSSEC DS records (values from your DNS host, which signs the zone):
namecom dnssec list acme.io
namecom dnssec create acme.io --algorithm 13 --digest-type 2 --key-tag 12345 --digest abc123Set up vanity nameservers:
namecom vanity-ns create acme.io --hostname ns1.acme.io --ips 1.2.3.4
namecom vanity-ns create acme.io --hostname ns2.acme.io --ips 5.6.7.8
namecom domain set-ns acme.io --ns ns1.acme.io,ns2.acme.ioCall an endpoint namecom does not wrap (namecom api, modelled on gh api):
namecom api /core/v1/domains/acme.io # the method defaults to GET
namecom api /core/v1/domains --paginate --jq '.domains[].domainName' # every page as one list
namecom api /core/v1/domains/acme.io/records -f host=www -f type=A -f answer=1.2.3.4 -F ttl=300 --dry-run
namecom api PUT /core/v1/domains/acme.io/records/123 --input record.json
namecom api -X DELETE /core/v1/domains/acme.io/records/123 --dry-run
namecom api /core/v1/hello --include # status line and headers, then the body
namecom api /core/v1/domains -i --jq '.totalCount' # headers as they came, then the filtered bodyThe method is GET, or POST when the request has a body (--data, --input,
-f or -F); name it first, or with -X/--method, to send anything
else. -f key=value adds a string, and -F key=value keeps true, false,
null and numbers as JSON and reads @file (or @-, stdin). Keys nest as
contact[firstName]=Ada, and ns[]=x appends to a list. On a GET the fields are query parameters instead.
--paginate follows nextPage and prints one document whose lists hold every
page's items, without nextPage and lastPage. --include prints the
status line and headers ahead of the body, and --jq and --fields filter
the body alone. Any method but GET and HEAD is previewed, not sent, under
--dry-run.
Scripting and automation:
# List every domain expiring within 60 days (GNU date; on macOS: date -v+60d +%F)
namecom domain list --all --expiring-before "$(date -d '+60 days' +%F)" -q
# Bulk-create an A record across all domains
namecom domain list --all -q | xargs -I{} namecom dns create {} --type A --answer 1.2.3.4
# '-' reads names from stdin, one per line (blank lines and # comments skipped)
namecom domain check - < names.txt
namecom domain list --all -q | namecom domain autorenew on - --yes # one request per domain, one confirmation
# Dry-run first, then apply
namecom dns create acme.io --type TXT --answer "v=spf1 include:sendgrid.net ~all" --dry-run
namecom dns create acme.io --type TXT --answer "v=spf1 include:sendgrid.net ~all" --yes
# Capture the new record's ID
ID=$(namecom dns create acme.io --type A --host api --answer 1.2.3.4 -q)
namecom dns delete acme.io "$ID" --yesRate limit. namecom paces itself to 10 requests a second (bursts of 5)
and retries a 429 with backoff, which leaves headroom under the API's limit
of 20 a second for the account. The limiter is per process: xargs -P 8
runs eight processes with eight limiters, which together send up to 80
requests a second, so the excess comes back as 429s and, once retries run
out, exit code 5. Prefer one process with many arguments, which is paced as
a whole:
namecom domain list --all -q | namecom domain check - # not xargs -P
namecom dns delete acme.io $(namecom dns list acme.io --type TXT -q) --yesFor commands that take one domain, run xargs without -P (one process
at a time); -P 2 already reaches the account's limit, and anything else
using the same account shares it.
Commands that change something ask first when run in a terminal. In a script
or a pipe there is no one to ask, so they stop with "confirmation required
for … — pass --yes to confirm when not running in a terminal" and exit 2
until you pass --yes.
Every command supports --output table, --output json, --output yaml
and --output tsv. The default is table in a terminal and json when
output is piped or redirected:
namecom domain list # rich table with colors and expiry urgency
namecom domain list --output json # machine-readable JSON
namecom domain list --output tsv # the table's columns, tab-separated
namecom domain list --quiet # one domain per line, for scriptingnamecom help formatting covers everything in this section, with examples.
--fields a,b,c keeps only those keys — the JSON keys -o json shows — of
each list item, or of the object a command prints, in that order. A list
keeps its {"data": [...]} envelope, so nextPage and total are still
there. It works with every -o: in a table or TSV the fields are the
columns.
namecom domain list --all --fields domainName,expireDate -o tsv --no-header |
while IFS=$'\t' read -r name expires; do echo "$name $expires"; done
namecom dns list example.com --fields id,type,host,answer -o tableAn item without a field gets null for it (an empty TSV cell): the API
leaves out empty values, so items do not all have the same keys. A field
that no item has is a usage error (exit 2) that lists the fields there are.
--fields names top-level keys only; reach into nested ones with --jq.
--jq <expr> runs a jq expression over the document -o json would print,
with an embedded jq (gojq), so jq need
not be installed. Each result prints on its own line: a string as itself,
without quotes (as jq -r and gh --jq print it), anything else as compact
JSON. gojq prints an object's keys sorted.
namecom domain list --all --jq '.data[] | select(.locked | not) | .domainName'
id=$(namecom dns create example.com --type A --answer 192.0.2.1 --yes --jq .id)
namecom dns create example.com --type A --answer 192.0.2.1 --dry-run --jq .body--jq means JSON: without -o it prints JSON in a terminal too, and with
-o table, yaml or tsv it is a usage error. With --fields, the fields
are picked first. A malformed expression is a usage error with gojq's
message, reported before the command sends anything. An expression that
fails on the output, and an unknown field, are usage errors too, with
nothing printed — except after a write, where the change has been made:
the output is printed unfiltered, with a warning, and the exit code is 0.
Both flags act on stdout only; a failing command prints its error envelope
on stderr with its usual exit code.
-o tsv prints a table's columns as tab-separated values, with a header row
unless --no-header. There is no colour, a date has no "(in 3 months)", and
a missing value is an empty cell, not "—". A backslash, tab, line feed or
carriage return in a value is written \\, \t, \n or \r, so every row
is one line. A command that shows one object (domain get) prints
field<TAB>value rows; a write prints its result's keys the same way
(changed<TAB>true); a dry run prints method, path and body (as
compact JSON) columns. status and version, whose terminal output is a
report rather than a table, print that report; use --fields or --jq
with them.
-q still wins over -o, tsv included. With --fields or --jq, which
choose what to print as well, it is a usage error.
-q/--quiet follows one rule whatever --output says: lists print one ID
or name per line, create commands print the new resource's ID, other writes
print nothing, and other reads print the one value a script most likely wants
(version the version, auth status the username, domain pricing the
price). Errors still go to stderr.
Tables drop their rightmost columns to fit a narrow terminal and say which
they hid; --wide keeps them all.
--dry-run prints the request a write would send, and sends nothing. In JSON
mode — including the default when piped — that is a JSON document with
dryRun, method, path and body keys; -o table prints
METHOD /path and the body instead.
With -o json — the default when output is piped — and with -o yaml,
which carries the same keys, output follows the rules below. A change to any
of them is a breaking change and is called out in the
CHANGELOG.
-
One document per stream. The result goes to stdout. stderr carries at most one document: the error envelope when the command fails, or
{"warnings": [...]}when it succeeded with something to say. -
Lists are
{"data": [...]}, withnextPageandtotaladded when the list is paged.datais[], nevernull, when there is nothing in it. This covers everylist, anddomain check,domain search,config list-profilesanddns exporttoo (dns importanddns syncread both that and the bare array older versions exported), anddomain getgiven several domains or-. -
One resource is the object itself, as the API returns it:
domain getwith one domain,dns create,email update. With--if-not-exists,dns createadds"changed"to the record:falsewhen it was already there,truewhen it was created. -
Keys are camelCase everywhere:
domainsTotal,dryRun,idempotencyKey. Values that name a kind of thing, such as error types (not_found) or dry-run actions (save_profile), are snake_case. -
A write with no resource to return prints
{"success": true, "changed": true, "message": "…"}.changedisfalsewhen the target was already in the requested state and nothing was sent —domain lock onfor a locked domain,dns delete --if-existsfor a record that is gone,dns import --skip-existingwith nothing new.messageis for people; branch onchanged, not on its wording. -
A write over several targets — a toggle given several domains,
dns deletewith several IDs — is still one document: the same three keys, withchangedtrue when any target changed, and one{"domain", "id", "changed", "message"}item per target underdata(domainoridas applies). One target prints the plain document above, so.changedreads either. -
A dry run prints
{"dryRun": true, "method": …, "path": …, "body": …}, with aquoteobject for a write that costs money. A dry run that plans several requests —dns import, a toggle ordns deleteover several targets — prints{"dryRun": true, "data": [ … ]};dns sync --dry-runadds its plan (creates,updates,deletes,kept,unchanged) beside thatdata. -
dns syncprints what it did:{"domain", "changed", "applied": [ … ], "unchanged"}. When a change fails, that document still goes to stdout, withfailed(andoutcomeUnknown: truewhen it may have gone through) andnotAttempted, and the error envelope goes to stderr. -
Warnings — a
--base-urlorNAMECOM_BASE_URLthat is not name.com, duplicate IDs dropped fromorder refund, records created before adns importfailed, an existing record's different TTL underdns create --if-not-exists— are not printed as text. They come out at the end, in the error envelope'swarnings, or as{"warnings": [...]}on stderr when the command succeeded. -
Nothing is HTML-escaped.
<,>and&print as themselves. -
Errors are one document on stderr:
{ "error": { "type": "not_found", "status": 404, "message": "Not Found", "hint": "check the name or ID for typos" } }typeis always there, and is one of:typeMeaning Exit code usageThe command line is wrong: an unknown command or flag, a bad argument or value 2 confirmation_requiredA write needs --yes, because there is no terminal to ask2 authCredentials missing, failing or rejected, or access denied (HTTP 401/403) 3 not_foundHTTP 404 4 rate_limitedHTTP 429, after the CLI's own retries 5 conflictThe thing already exists (the API answers a duplicate DNS record with a 400 that says so), or HTTP 409, which the API uses for a reused idempotency key 1 abortedA confirmation was declined or a prompt cancelled 1 networkNo HTTP response: a timeout, or a connection that failed 1 apiAny other failure: another API error, or a local one such as an unreadable file 1 statusis the HTTP status, present only when the API answered.messageandhintare for people. The other keys appear only when they apply:detailsholds structured detail (the raw response body fornamecom api, the profile, username, endpoint and config file for a rejectedauth status, with where each came from asusernameSource,tokenSourceand so on), andsuggestionsthe full command lines an unknown command was probably meant to be (["namecom dns delete"]).idempotencyKeyis set when a write's outcome is unknown (exit 6): theX-Idempotency-Keythe request carried. The envelope also has a top-levelhint, a copy oferror.hintwhere older versions put it. It is deprecated, kept for this release only so scripts can move toerror.hint.
namecom api is the one exception: it prints the API's response body exactly
as received ({"domains": [...]}, not {"data": [...]}). It exists to reach
endpoints namecom does not wrap and to show what the API itself says, and
reshaping the body would hide the very thing it was asked for. Its errors use
the envelope above, with the response body as details.
Credentials are written by namecom auth login to your platform's user config directory:
| Platform | Location |
|---|---|
| macOS | ~/Library/Application Support/namecom/config.yaml |
| Linux | $XDG_CONFIG_HOME/namecom/config.yaml, or ~/.config/namecom/config.yaml |
| Windows | %AppData%\namecom\config.yaml |
namecom auth status prints the path in use. An older ~/.namecom/config.yaml is still read if the current location has no config, and NAMECOM_CONFIG overrides both. Multiple profiles are supported for managing separate accounts:
namecom auth login --profile work
namecom auth login --profile personal
namecom domain list --profile work
namecom config use work # make it the defaultSandbox vs. production — test changes safely against name.com's sandbox API before running them for real:
namecom auth login --profile sandbox --sandbox
namecom domain register test.com --profile sandbox--sandbox at login saves the profile as a sandbox one, so every command run
with it targets api.dev.name.com. The sandbox has its own API token,
separate from your production one. Omit --profile to use your default
(production) profile.
Environment variables (useful in CI; namecom help environment lists them all):
export NAMECOM_USERNAME=yourname
export NAMECOM_TOKEN=yourtoken
export NAMECOM_SANDBOX=true # target sandbox API (true/false, yes/no, on/off, 1/0)
export NAMECOM_PROFILE=staging # select a profile
export NAMECOM_CONFIG=~/namecom-ci.yaml # use this file instead of the default
export NAMECOM_BASE_URL=http://127.0.0.1:8080 # a local stub; --base-url overrides it
export NAMECOM_NO_UPDATE_NOTIFIER=1 # never print the "new release" notice
namecom domain listNAMECOM_USERNAME and NAMECOM_TOKEN are enough on their own: no config file
or profile is needed. namecom config show and namecom auth status resolve
credentials exactly as other commands do and say where each value came from
(env NAMECOM_TOKEN, flag --username, profile work, token_cmd); in JSON
that is a sibling key such as "usernameSource": "env NAMECOM_USERNAME".
CI — a GitHub Actions job needs only the two variables, from repository
secrets. auth status checks them first, so a bad token fails the job before
any real work:
jobs:
dns:
runs-on: ubuntu-latest
env:
NAMECOM_USERNAME: ${{ secrets.NAMECOM_USERNAME }}
NAMECOM_TOKEN: ${{ secrets.NAMECOM_TOKEN }}
NAMECOM_NO_UPDATE_NOTIFIER: "1"
steps:
- uses: actions/setup-go@v5
with:
go-version: stable
- run: |
go install github.com/patramsey/namecom-cli@latest
mv "$(go env GOPATH)/bin/namecom-cli" "$(go env GOPATH)/bin/namecom"
- run: namecom auth status
- run: namecom dns create example.com --type TXT --host _verify --answer "${{ vars.VERIFY_TOKEN }}" --yesTo write a profile without a terminal instead — for a later step, or a
machine image — pipe the token to --with-token, or save a credential
helper with --token-cmd. Both check the credentials with the API first
(--no-verify skips that), and replacing an existing profile needs --yes:
echo "$NAMECOM_TOKEN" | namecom auth login --username alice --with-token
namecom auth login --username alice --token-cmd 'op read op://vault/namecom/token' --profile ciSecret manager integration — add token_cmd to your config and credentials are fetched at runtime, never stored on disk:
profiles:
default:
username: yourname
token_cmd: "op read op://vault/namecom/token" # 1Password exampleThe command must print the token on a single line. It runs through sh -c on
macOS and Linux, and through cmd.exe on Windows. To use sh syntax on
Windows, call sh yourself — for example
token_cmd: sh -c "op read op://vault/namecom/token | tr -d '\r'" — with an
sh on your PATH (Git for Windows ships one).
The Homebrew formula installs completions for bash, zsh and fish. For other installs, write the script to a directory your shell reads; these need no root:
# bash (needs the bash-completion package, v2)
mkdir -p ~/.local/share/bash-completion/completions
namecom completion bash > ~/.local/share/bash-completion/completions/namecom
# zsh: then add `fpath=(~/.zfunc $fpath)` to ~/.zshrc, before `compinit` runs
mkdir -p ~/.zfunc
namecom completion zsh > ~/.zfunc/_namecom
# fish
mkdir -p ~/.config/fish/completions
namecom completion fish > ~/.config/fish/completions/namecom.fishPowerShell: add namecom completion powershell | Out-String | Invoke-Expression
to your $PROFILE.
Open a new shell afterwards. namecom completion <shell> --help has more.
| Flag | Default | Description |
|---|---|---|
-o, --output |
table in TTY, json otherwise |
Output format: table, json, yaml, tsv |
--fields |
Keep only these keys of each list item, or of the object, in this order — see Picking fields, jq, and TSV | |
--jq |
Filter the JSON output with a jq expression; strings print unquoted | |
-q, --quiet |
Script output: lists print one ID or name per line, creates the new ID, other writes nothing — see Output formats | |
-y, --yes |
Skip all confirmation prompts; required for writes when not in a terminal | |
--dry-run |
Print the request a write would send, without sending it — a JSON document in JSON mode. Reads are unaffected | |
--profile |
Use a named credential profile | |
--sandbox |
Target the sandbox API (api.dev.name.com) |
|
--base-url |
Send requests to another API base URL, such as a local stub or a proxy (overrides NAMECOM_BASE_URL). Your credentials go wherever it points; a warning says so when it is not name.com |
|
--color |
auto |
Colorize output: auto, always, never |
--wide |
Keep every table column, even when the table is wider than the terminal | |
--timeout |
30s |
Total time budget for one API call, retries included |
--debug |
Log HTTP requests/responses to stderr (token and auth codes redacted) | |
--debug-file |
Log HTTP requests/responses to a file (appends; useful as an audit log) | |
--no-header |
Omit the header row from table and TSV output | |
--idempotency-key |
a fresh key per write | Pin every write in this invocation to one key, so re-running the same command after a failure can be recognized as a retry by endpoints that honor idempotency keys — see Idempotency keys |
--username |
API username (overrides config and NAMECOM_USERNAME) |
|
--token |
API token (overrides config and NAMECOM_TOKEN) |
| Code | Meaning |
|---|---|
0 |
Success |
1 |
API or other runtime error, a confirmation declined or a prompt cancelled (Ctrl-C), or — with domain check --exit-status — a name that is not available |
2 |
Usage error: an unknown command or flag, a wrong number of arguments, or an invalid value |
3 |
Authentication: credentials missing (an unknown --profile included), failing or rejected, or access denied (HTTP 401/403) |
4 |
Not found (HTTP 404) |
5 |
Rate limited (HTTP 429), after the CLI's own retries |
6 |
Write outcome unknown: a request that changes something got a 5xx, or timed out or lost its connection after it was sent, so it may or may not have been carried out — see Idempotency keys |
With --output json or yaml — including the JSON default when piped — an
error is written to stderr as one document, an error object whose type
says which of these it is. See JSON contract.
Every POST, PUT and DELETE namecom sends carries an X-Idempotency-Key
header: a fresh key per request, or, with --idempotency-key, the key you
name for every write in that invocation. PATCH (domain update) carries
none.
When a write fails in a way that leaves its outcome unknown — the API
answered 5xx, or the request timed out or lost its connection after it was
sent — namecom exits 6 and names the key it used: in the hint
(outcome unknown; re-run with --idempotency-key <key>) and, in JSON mode,
as error.idempotencyKey. A POST is never retried on a 5xx, because the
server may already have done the work. Check whether the change was made;
if it was not, re-run the same command with --idempotency-key <key>.
dns sync is the exception: it is not re-run with the key, since the next
run sends different requests, but simply run again. It plans from the live
zone, so a change that did land is not repeated. Its result document marks
the failed change outcomeUnknown, and its hint says to run sync again.
Whether the key prevents a duplicate depends on the endpoint. The Core API declares the header on five operations:
| API operation | namecom command |
|---|---|
CreateDomain |
domain register, and the register domain check offers |
ProcessRefund |
order refund |
VerifyContact |
contact verify |
ResendContactVerificationEmail |
contact resend |
PurchasePrivacy |
none (domain privacy on uses a different endpoint) |
The API reference describes what a reused key does only for refunds: the
same key returns the original response instead of refunding again. For the
other four the header is declared but what the API does with it is not
documented. Every other write — DNS records, email and URL forwarding,
vanity nameservers, DNSSEC, transfers, renewals, nameserver and contact
changes — ignores it. Two dns create requests under one key made two
records in the sandbox. For those, check before you retry.
make build # compile to ./namecom
make test # go test ./...
make lint # golangci-lint runThe API client is github.com/namedotcom/core-api-go, name.com's own SDK. There is no code generation step.
Contributions welcome — see CONTRIBUTING.md for the setup, the checks CI runs, and what to know before touching the API client or a command that writes. Participation is governed by the Code of Conduct.
Security issues should go through private vulnerability reporting rather than a public issue — see SECURITY.md.
Release history is in CHANGELOG.md.
MIT — see LICENSE.
