Skip to content
Docs · Web

Domains

Buy domains inside Harakumo, paid from the Balance, connect domains you own by nameservers, edit DNS, auto-renew from the Balance, and transfer in or out.

View as Markdown
All topics
On this page

Every domain in the workspace is one entry, keyed by its root (GET /api/domains), with one plain-word state and the next step. www is served together with the apex.

myshop.comcheckmyshop.com — takenmyshop.dev — availableconnect → projectsearch, register, connect

In plain words

Your own web address, like yourshop.com. Search for one, buy it from your balance and it is set up to point at your project, or connect one you already own.

For example: A bakery buys freshbakes.com, and its site answers there.

Buy, connect or transfer?

You want toDo this
A new nameBuy it here. It is paid from the Balance, and can connect to a project in the same call.
Use a name you own elsewhereConnect it: add it to a project and switch its nameservers to the two Harakumo gives you.
Move a name you own to HarakumoTransfer it in with its auth code; the price includes a year of renewal.

Buying

  1. Search: GET /api/domains/search?q=myshop (or myshop.org to search that name first). Each result has available (null means it could not be checked — not taken), sellable, price_cents, renewal_price_cents and, for a name you can buy, lock_lifts_at.
  2. Buy: POST /api/domains/registrations { domain, projectId? }. The live price is checked again; if it is above the price you saw (expectedPriceCents) nothing happens.
  3. With a projectId the domain connects automatically and goes live within minutes to hours as DNS settles; the owner gets an email when it is live.
  • Endings sold today: .com, .dev, .app, .io, .ai, .cloud. Automatic purchases above $100 a year are refused.
  • Every domain is paid from the Balance at the price shown before you buy, on every plan. There is no cap on how many you buy — each is paid for.
  • A Balance too small answers 402 insufficient_balance with the shortfall and add_funds_url, the page where funds are added; nothing is charged. The card on file is charged only when the owner, signed in to the dashboard, asks (payWithCard: true): see Who pays, and how. That answer offers the card (pay_with_card) only to the owner signed in to the dashboard; anyone else, an AI connector or API key included, gets pay_with_card: null and is told the owner pays by card on the Domains page.
  • Purchases count toward the workspace's monthly spending limit, if one is set.
  • Search results and renewal prices come from the registrar's price list, which Harakumo refreshes once a day; a purchase checks the live price again before anything is charged.
  • Harakumo registers the domain through its registrar account and manages it for your workspace. Harakumo is the registrant of record (the registration lists Harakumo, not you, as the holder, and your details are not sent to the registrar); your workspace is the record that the domain is yours, and you can transfer it out to hold it in your own name. Registries lock a domain for 60 days after registration or transfer.
  • That lock means a domain you buy cannot move to another registrar until lock_lifts_at, 60 days after the purchase; it works as usual meanwhile. The buy dialog says so before you pay, and search results, the dry run (dryRun: true), the purchase answer and every registration carry the date.
  • Prices are in US dollars and do not include taxes such as sales tax, VAT or GST.

Who pays, and how

  • Anyone who manages domains: developers, admins and owners (in the dashboard, or through their AI connector with write access) and full workspace API keys. AI connectors and API keys always pay from the Balance.
  • The card is the owner's, so it takes the owner in person. An AI connector, the owner's own included, is a long-lived token another app holds, and an API key belongs to an app: asked to use the card, any of them that manages domains gets card_requires_owner (402, or 403 for cardBackup) with dashboard_url, the Domains page where the owner can do it, and nothing is charged or changed. A read-only connection or read key is told it is read-only.
  • When the Balance is short, an AI connector or API key gets add_funds_url to hand to the person, who adds funds in a browser; then the call is made again. An owner's AI connector can also make a checkout link for that (create_add_funds_link).
  • In a workspace another workspace's plan covers, its domains are paid from its own Balance, under its own spending limit, not from the paying workspace's (which pays for its AI): add_funds_url opens that own Balance (Billing → Balance), add_funds_api is POST /api/billing/balance { "cents": 1000, "own": true } (it charges the card on file, so it takes the owner signed in: an AI connector or API key that calls it gets dashboard_only, whose dashboard_url is that own Balance), and the checkout link takes own: true (create_add_funds_link, or POST /api/billing/balance/checkout { "cents": 1000, "own": true }), which a connector can make for the owner to open and pay.
CallFrom the BalanceWith the card on file
Buy: POST /api/domains/registrationsAnyone who manages domainspayWithCard: true, or autoRenewCard: true for its renewals: the owner signed in to the dashboard
Renew now: PATCH /api/domains/registrations/:id { action: "renew" }Anyone who manages domainspayWithCard: true: the owner signed in to the dashboard
Auto-renew: PATCH /api/domains/registrations/:id { action: "auto-renew", enabled }Anyone who manages domainscardBackup: true: the owner signed in to the dashboard
Transfer in: POST /api/domains/transfersAnyone who manages domainspayWithCard: true: the owner signed in to the dashboard

Connecting a domain you own

  1. Add it to a project: POST /api/projects/:id/domains { hostname, connect: true }. Harakumo checks no other workspace owns that root domain. Adding the domain itself (example.com) serves www.example.com too, unless www is added as a hostname of its own; a subdomain serves only itself.
  2. Harakumo copies the records your domain answers with today (email included) into its DNS, and returns two nameservers. Review the copied records in DNS before switching.
  3. Set those nameservers at your registrar. The domain goes live on its own within about 10 minutes of the change being visible — there is nothing to press.

Switching nameservers moves all DNS for the root domain, even when you only connect a subdomain. Connecting by CNAME is not available yet.

DNS records

Any domain whose DNS Harakumo hosts — bought, transferred in or connected by nameservers — has editable records at /api/domains/zones/{root}/dns (GET, POST, PATCH, DELETE), and the same DNS panel under Manage on the Domains page. Record types: A, AAAA, CNAME, MX, TXT and NS. SRV and CAA are not supported yet.

Add an MX record
curl -X POST https://harakumo.com/api/domains/zones/myshop.com/dns \
  -H "Authorization: Bearer $HARAKUMO_API_KEY" -H "content-type: application/json" \
  -d '{ "type": "MX", "name": "@", "content": "mail.example.net", "priority": 10 }'

Want email from this domain? Mail → Sending domains sets it up (automatically when its DNS is here).

Renewal

  • Bought domains auto-renew by default (autoRenew: false at purchase to opt out). Renewal happens in the last 7 days before expiry, at renewal_price_cents, paid from the Balance. Warnings go out 30, 7 and 1 days before.
  • The card covers a renewal only if the owner, signed in to the dashboard, turned on card backup (autoRenewCard: true at purchase, or PATCH { action: "auto-renew", enabled: true, cardBackup: true }). Renewals set up by developers, API keys, AI connectors or transfers pay from the Balance only.
  • A domain past expiry is marked expired; renewing it within 30 days points it back at its DNS.
  • Every renewal is paid at its renewal price, on every plan. The renew answer says what was charged and where from (chargedCents, paidFrom), and the expiry email quotes the price of an automatic renewal.
  • Renewals above $100 a year are never automatic: the daily renewal, the API, AI connectors and other roles stop there (403 over_cap, with dashboard_url), and the owner is emailed. The workspace owner, signed in to the dashboard, renews it by hand on the Domains page at the price shown; that call carries expectedPriceCents at or above the price (409 confirm_price without it, 409 price_moved when the price went up).

Stop serving, DNS only, other nameservers

  • DELETE /api/domains/registrations/:id (or PATCH { action: "detach" }) stops serving the project. DNS, email and renewal stay.
  • PATCH { action: "host-dns" } hosts DNS here without a project; { action: "set-nameservers", nameservers: [...] } points the domain at another provider; { action: "connect", projectId } serves another project.
  • Deleting a project detaches its domains; bought domains stay in the workspace and keep renewing.
  • GET /api/domains/registrations/:id?include=registrar adds at_registrar: what the registrar holds for the domain right now, read live — its transfer lock (true, false, or null when it could not be read) and the nameservers it points at, whether those are this domain's Harakumo DNS and who runs them. The Domains page shows the same for every domain the workspace holds. The lock is not changed here: a transfer-out request has it lifted.

Transfers

  • In: unlock the domain at your current registrar, get its auth code, and POST /api/domains/transfers { domain, authCode, dryRun: true } to validate and quote without paying. Then send it again without dryRun (with expectedPriceCents). The Balance pays once (the card only when the owner, signed in to the dashboard, sends payWithCard: true); the transfer completes in 5–7 days.
  • Out: PATCH /api/domains/registrations/:id { action: "transfer-out" }. The auth code arrives within five calendar days (usually sooner), by email and in the dashboard. The domain keeps working until it has moved.

Limits

  • Your own domains connected per plan: 2 hobby, 20 pro, 100 team, 500 scale.
  • Up to 10 new DNS zones per workspace a day, and 20 a day across all the workspaces one person owns. Connecting a domain the workspace already holds is never refused by either.
  • Domain routes are workspace-wide: use a workspace key. Adding a hostname to a project (/api/projects/:id/domains) accepts that project's key.

Reference

SDK
const { results } = await hk.domains.search('myshop');     // [{ domain, available, sellable, price_cents, renewal_price_cents }]
const pick = results.find(r => r.available && r.sellable);
const { registration, paidFrom } = await hk.domains.register(pick.domain, { projectId: project.id, expectedPriceCents: pick.price_cents });
// 402 insufficient_balance → err.body.shortfall_usd, err.body.add_funds_url

await hk.domains.add(project.id, { hostname: 'app.myshop.dev' });   // a domain you own; connect: see REST
const { registration: one } = await hk.domains.registration(registration.id);   // renewal_price_cents, lock_lifts_at
const check = await hk.domains.transferIn('myshop.com', { authCode: 'EPP-CODE', dryRun: true });