# DNSMint: full reference This file is the complete DNSMint reference in one document, written for machine ingestion. The human version lives at https://dnsmint.com/quickstart and the OpenAPI 3.1 spec at https://dnsmint.com/openapi.json. ## What DNSMint is DNSMint is a hostname registration service: an authenticated API call registers an IP address and returns a stable hostname on a domain DNSMint operates, served by DNSMint's authoritative DNS. The hostname is an opaque subdomain that does not encode the IP, stays the same when the registered IP changes, and works with any ACME client, so Caddy or certbot can get a free certificate for it. DNSMint can also run ACME on the hostname's behalf, holding the certificate and renewing it, for callers with no ACME client to run. DNSMint never proxies or carries customer traffic; connections go straight from clients to the registered server. ## Authentication Every API request needs an API key in the Authorization header: Authorization: Bearer dnsm__ Keys are created in the dashboard at https://dnsmint.com/dashboard. The secret is shown once, at creation, and stored only as a hash after that. Keys can be revoked from the dashboard at any time. A missing, malformed, or revoked key returns 401: { "error": "Missing or invalid API key", "code": "UNAUTHORIZED" } ### Scopes A key carries scopes, chosen when it is created: hostnames:read List hostnames, and read one. hostnames:write Mint a hostname, repoint its IP, release it. dns01:write Mint acme-dns credentials for a hostname. Each scope also carries what it reaches - the whole organization, one domain, or one hostname: dns01:write every hostname on the organization dns01:write @ example.dev every hostname under one domain dns01:write @ q7k4m2.example.dev one hostname A key that authenticates but lacks the scope an endpoint requires returns 403: { "error": "This API key does not carry the \"hostnames:write\" scope", "code": "FORBIDDEN" } A key that carries the scope but is narrowed away from the hostname in the request gets the same status, naming what it does reach: { "error": "This API key's \"dns01:write\" scope is limited to q7k4m2.example.dev", "code": "FORBIDDEN" } A narrowed `hostnames:read` key gets a shorter list from GET /hostnames rather than an error; `active` and `cap` in that response stay organization-wide, because they are the quota you check before minting. Scope to a domain when the caller works across a namespace. A key limited to `example.dev` can mint, repoint, release and publish challenges for every hostname under that domain, including ones minted later, and reaches nothing else on the organization. That is the shape for a deploy pipeline, a cluster, or any account that holds more than one domain. Scope to a hostname when the caller is one machine. Give the box that renews a certificate `dns01:write` on its own hostname and nothing else: it can obtain that machine's certificate and cannot mint a credential for anything else you run, repoint an IP, or release a name. That is the shape to use when an ACME client reads the API key directly rather than a pre-seeded acme-dns credential. A key created without naming any scope gets all three on the whole organization - never `keys:write`, which must be asked for explicitly. ### Managing keys from the API A key carrying `keys:write` can create and revoke keys through `/v1/keys`, so credentials rotate without a person. Rotation is mint the replacement, deploy it, then revoke the outgoing key. Three rules bound it. A key can only grant within its own reach, so one narrowed to a domain cannot create an organization-wide key or reach a domain it does not hold. The scope is organization-wide or nothing, because a key is an organization-level object. And it cannot share a key with `dns01:write`, the scope meant for an exposed web server - a machine renewing certificates should not also be able to issue itself credentials. The last two together would leave `dns01:write` un-mintable from the API, so `hostnames:write` may hand out `dns01:write` on a hostname it reaches without holding it. What that hands over is wildcard issuance for a name the holder can already repoint or release, so the grant must name a hostname: per-machine keys are the point, and no domain-wide or organization-wide DNS-01 key can be created through the API. The granting key still cannot publish a challenge, and the minted key still cannot mint another. Revoking a key does not revoke keys it created, because rotation is mint-then-revoke and a cascade would destroy the replacement. Every listing carries `created_by_key_id`, and account activity records which key performed each hostname action - together, that is what makes a leaked key containable: its blast radius is a list rather than a guess. ### POST /v1/keys (create) Mints a key and returns the secret once; no other response ever contains it. Requires keys:write. Body: name (optional, up to 60 characters, default "api"), scopes (optional: scope strings for the whole organization, or objects narrowing one to a domain or hostname; omitted, the key gets hostnames:read, hostnames:write and dns01:write on the organization), expires_in_days (optional, 1 to 3650; omitted for a key that does not expire). curl -X POST https://dnsmint.com/api/v1/keys \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "worker-7", "scopes": [{"scope": "dns01:write", "hostname": "q7k4m2.example.dev"}], "expires_in_days": 30}' { "key": "dnsm_9b21c5f04a7e_1f3d...", "key_id": "9b21c5f04a7e", "name": "worker-7", "scopes": [{"scope": "dns01:write", "on": "q7k4m2.example.dev"}], "created_at": "2026-09-05T12:00:00.000Z", "last_used_at": null, "expires_at": "2026-10-05T12:00:00.000Z", "created_by_key_id": "4e378b700a9e" } Errors: 400 invalid scopes, an escalation attempt, or the key cap reached; 401; 403 key does not carry keys:write. ### GET /v1/keys (list) Every live key on the organization, metadata only. created_by_key_id is null when a person created the key in the dashboard and names the minting key when a program did. curl https://dnsmint.com/api/v1/keys \ -H "Authorization: Bearer $DNSMINT_KEY" Errors: 401, 403 key does not carry keys:write. ### DELETE /v1/keys/{key_id} (revoke) The key stops authenticating on its next request. A key may revoke itself. key_id is the middle segment of the key string, between dnsm_ and the secret. curl -X DELETE https://dnsmint.com/api/v1/keys/9b21c5f04a7e \ -H "Authorization: Bearer $DNSMINT_KEY" {"key_id": "9b21c5f04a7e", "revoked": true} Errors: 401, 403 key does not carry keys:write, 404 no live key with that id in this organization (an unknown, revoked, or foreign id all answer 404). ### PUT /v1/keys/{key_id} (rename) Changes the label and nothing else. A key's scopes are fixed for its life: the secret is bound to the grants it was minted with, so widening one in place would give new reach to every holder of a credential issued narrow. To change what a key may do, mint its replacement and revoke it. A key may rename itself. curl -X PUT https://dnsmint.com/api/v1/keys/9b21c5f04a7e \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "ci-deploy"}' {"key_id": "9b21c5f04a7e", "name": "ci-deploy"} The name is trimmed and truncated to 60 characters, the same rule create uses. Errors: 400 name missing or blank after trimming, 401, 403 key does not carry keys:write, 404 no live key with that id on this account. ### POST /v1/billing/checkout (buy more capacity) Returns a link the account owner opens to raise the account's limits, for when a mint is refused because the account is at its hostname cap or has no plan. Requires billing:write. Body: hostnames (optional), domains (optional); both default to what the account already holds, so asking for hostnames alone keeps the domains. curl -X POST https://dnsmint.com/api/v1/billing/checkout \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"hostnames": 250}' { "checkout_url": "https://polar.sh/checkout/co_9f2a", "amount_cents": 4900, "selection": {"domains": {"starter": 2}, "hostnames": 250} } It charges nothing. The purchase happens only if a person opens the URL and enters card details, which is what makes it safe to give an unattended program. An account that already has a plan gets 409 carrying manage_url instead, because changing a running plan bills the card already on file and that is a person's decision. The link carries the numbers asked for, so the plan panel opens on them. billing:write is team-wide or nothing, is never granted to a key created without naming scopes, and is the one account-level scope an OAuth connector can hold - a checkout link is completed by a person and leaves the connector holding nothing, where a minted key would outlive the consent that created it. The MCP tool is start_checkout. Errors: 400 a count that is not a whole number or above what a plan can hold; 401; 403 key does not carry billing:write; 409 a plan already exists, or the selection is below what the account currently uses; 429; 503 billing is not configured or the payment provider did not answer. ### GET /v1/billing/checkout The read half, scoped hostnames:read. It reports and never acts, so a caller handed a payment link waits on this instead of retrying a mint to find out whether it was completed: { "plan": { "active": false, "name": "Starter" }, "hostnames": { "active": 5, "cap": 5 }, "domains": { "held": 1, "cap": 1 }, "pending_checkout": { "started_at": "2026-09-08T04:12:00.000Z" } } pending_checkout appears only while no plan is active; once one exists the question is answered by plan. The MCP tool is get_plan. ### Expiry A key can be given a lifetime when it is created. An expired key stops authenticating and returns a distinct code, naming the date it lapsed, so it is never confused with a malformed or revoked credential - retrying will not help, and the fix is a new key rather than a corrected header: { "error": "This API key expired on 2026-09-05. Create a new key to continue.", "code": "KEY_EXPIRED" } Set one when the caller will not outlive it: an agent's sandbox, a CI run, a device on trial. The credential then retires with the thing it was minted for, the way the hostname already does. Keys created without a lifetime do not expire, which is the right answer for a long-running caller, and is what every key created before this feature still does. ## API Base URL: https://dnsmint.com/api/v1. Requests and responses are JSON. Request bodies are capped at 4KB. A hostname object looks like this in every response that returns one: { "id": "host_01937d3e8f217c4ab8e24f9a1c3d5e6b", "hostname": "q7k4m2.a3f9c1-d4e7b8.dev", "status": "pending", "certificate": "self", "created_at": "2026-08-26T08:30:00.000Z" } Fields: id (opaque identifier used in URLs), hostname (fully qualified name), status ("pending", "live", "released", "suspended", or "terminated" - see Lifecycle rules), certificate ("self", "managed" or "csr") and created_at (ISO 8601). New registrations may briefly return "pending"; poll GET until "live". There is no address in the response, by design. What a hostname points at is either what you just sent or what DNS answers, and DNS is the authoritative copy; a second one here could only disagree with the zone. Ask GET /v1/hostnames/{id}/diagnose for what is actually being served. ### POST /v1/hostnames (create) Registers an address and mints a new hostname. Send "ip" (an IPv4 or IPv6 address, public or private) or "target" (a platform endpoint to follow, for a platform that gives you a URL and no IP), not both. A target is resolved and published as ordinary A or AAAA records, then followed within its TTL. What you mint with fixes the hostname's address class for life, whichever field you sent. Optional body field "subdomain" picks a custom subdomain, on every plan. Optional "domain" selects one of the organization's dedicated domains, which must already be assigned to it; omitted, we pick one. Optional "certificate" is "self", "csr" or "managed", default "self". Optional "ca" selects the certificate authority for "managed" and "csr" - the modes where DNSMint runs the ACME client: "letsencrypt" (default) or "google". Sending it with "self" is a 400, because there you run the ACME client and the CA is a choice you make in it. Omitted, we pick Let's Encrypt unless it has recently rate-limited orders for your domain. Renewals always go back to the CA that issued. See https://dnsmint.com/ca Returns 201. curl -X POST https://dnsmint.com/api/v1/hostnames \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"ip": "34.120.50.10"}' { "id": "host_01937d3e8f217c4ab8e24f9a1c3d5e6b", "hostname": "q7k4m2.a3f9c1-d4e7b8.dev", "status": "pending", "certificate": "self", "created_at": "2026-08-26T08:30:00.000Z" } Errors: 400 (bad JSON, neither "ip" nor "target", both of them, a target that does not resolve or answers both public and private addresses, invalid address, invalid subdomain, domain or certificate mode, body over 4KB, domain not assigned to this account), 401, 403 (the key does not carry hostnames:write, or is narrowed to another domain), 409 (subdomain taken or retired; or DOMAIN_PROVISIONING, a domain for this account is still being registered; or NO_DOMAIN_AVAILABLE, none is assigned and none is being registered, so a timed retry will not help), 429 (at the active-hostname cap, or over 30 registrations in a minute), 501 (certificate: "managed" where the deployment has no certificate encryption key), 500. ### GET /v1/provisioning The other half of DOMAIN_PROVISIONING. That error says to retry shortly; this says when retrying will work, so nothing has to poll the create endpoint to find out. Reading it buys, claims and promotes nothing, so polling it cannot turn into spending. Scoped hostnames:read. { "state": "provisioning", "domain": "a3f9c1-d4e7b8.dev", "since": "2026-09-08T04:12:00.000Z", "delegated": false, "retry_after": 30 } state is none (nothing being bought, so retrying will not help), provisioning, ready (bought and delegated, waiting to be claimed) or claimed (minting works). delegated is the gate that decides readiness: state is our own bookkeeping and only delegation proves the parent zone points at our nameservers. retry_after also arrives as a Retry-After header, and is absent rather than zero when there is nothing left to wait for. Errors: 401; 403 the key does not carry hostnames:read. A "csr" body field on this call is a 400. The CSR must name the hostname, and this call is what mints it, so there is no name to sign over yet. Register with certificate: "csr", then POST the request to /v1/hostnames/{id}/certificate. ### Certificate mode The field decides who holds the private key, and that carries who runs the ACME client and who talks to the CA: mode private key ACME client talks to CA renewal self you you you yours csr you DNSMint DNSMint yours managed DNSMint DNSMint DNSMint ours, automatic "self" means you hold the private key: run an ACME client against the DNS-01 API and we never see key material. It is the default. "managed" means DNSMint runs ACME for the hostname: we generate the keypair, publish the DNS-01 challenge, and renew. You get the certificate and its key, and DNSMint keeps a copy so the current pair can be fetched at any time. Every renewal mints a fresh keypair, so a managed key is never older than one certificate. Fetch both from GET /v1/hostnames/{id}/certificate. Delivery is pull only - nothing is ever pushed to your infrastructure. Renewal timing follows the CA's ACME Renewal Information (RFC 9773), so renewals cost nothing against issuance rate limits. Returns 501 on a deployment with no certificate encryption key configured, which is a deployment gap rather than a bad request. "csr" means you hold the private key and DNSMint runs the protocol. Register the hostname, then POST a PKCS#10 certificate request to /v1/hostnames/{id}/certificate. DNSMint places the ACME order, publishes the DNS-01 challenge, and stores the issued chain; GET the same path to fetch it. The response carries no private_key, because DNSMint never had it. Renewal on "csr" is yours. DNSMint holds no key for the hostname, so it cannot build a replacement request. Reusing the original request forever would pin one keypair to the name for its whole life, and asking you for a new one would mean connecting to your infrastructure, which DNSMint does not do. Watch expires_at and POST a fresh request before it passes. First issuance and every renewal are the same call. No certificate encryption key is needed for this mode: a CSR and a chain are both public. The optional "ca" field picks the issuer for "managed" and "csr": "letsencrypt" (default) or "google". Sending it with "self" is a 400 - there you run the client, so the CA is a choice you make in it. Omitted, DNSMint picks Let's Encrypt unless it has recently rate-limited orders for your domain. Renewals always return to the CA that issued, because the ACME Renewal Information identifier is only resolvable by that CA. See https://dnsmint.com/ca ### GET /v1/hostnames (list) Lists the organization's hostnames, newest first. Released and terminated hostnames are excluded; suspended ones are shown. Query parameters: limit (default 100, clamped to 1..500) and skip (default 0), for pagination. The envelope also returns total (non-released count), active (live hostnames, which the create cap counts), cap (plan maximum), and the limit and skip values actually applied. Another page exists when skip + limit < total. curl "https://dnsmint.com/api/v1/hostnames?limit=100&skip=0" \ -H "Authorization: Bearer $DNSMINT_KEY" { "hostnames": [ { "id": "host_01937d3e8f217c4ab8e24f9a1c3d5e6b", "hostname": "q7k4m2.a3f9c1-d4e7b8.dev", "status": "pending", "certificate": "self", "created_at": "2026-08-26T08:30:00.000Z" } ], "total": 1, "active": 1, "cap": 5, "limit": 100, "skip": 0 } Errors: 401, 500. ### GET /v1/hostnames/{id} (read one) Returns one hostname. New registrations may briefly return "pending"; poll GET until "live". curl https://dnsmint.com/api/v1/hostnames/host_01937d3e8f217c4ab8e24f9a1c3d5e6b \ -H "Authorization: Bearer $DNSMINT_KEY" Response: 200 with the hostname object. Errors: 401, 404 (no hostname with this id in this organization), 500. ### PUT /v1/hostnames/{id} (set the IP) Puts an IP on the hostname. If the address is unchanged, nothing changes and DNS is not rewritten. If it changed, the record is replaced and the hostname stays live. The hostname and any certificates issued for it are unchanged. Body: {"ip": "
"} or {"target": ""}, exactly one. curl -X PUT https://dnsmint.com/api/v1/hostnames/host_01937d3e8f217c4ab8e24f9a1c3d5e6b \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"ip": "34.120.51.22"}' Response: 200 with the hostname object. Send `target` instead of `ip` to follow a platform endpoint that publishes no address of its own, such as a Railway service. We resolve the target and serve ordinary A and AAAA records refreshed within its own TTL, so the hostname never carries a CNAME: your certificates, your CAA and every record under the name keep working. A hostname keeps the address class it was minted with, and that is re-checked every time we refresh rather than only when you set it: a target that starts answering with a private address is refused, and the hostname stays on its last public one. That check is what makes this safe, so any target is accepted. Setting `ip` again takes the hostname off its target. The hostname object carries no address and no target. Where a hostname points is either what you just sent or what DNS answers, and a copy in the response body can only disagree with the zone. Ask the diagnose endpoint for where it points, what we observed, and when. Errors: 400, 401, 403 (private or reserved IP on a changed address, or the hostname is suspended), 404, 409 (the hostname was released or terminated), 429 (over 60 updates in a minute), 500. ### DELETE /v1/hostnames/{id} (release) Releases the hostname. The DNS record stops being served and any certificate we hold for it is revoked. The subdomain returns to your account and can be minted again as a new hostname. Releasing an already released hostname returns the same success response. curl -X DELETE https://dnsmint.com/api/v1/hostnames/host_01937d3e8f217c4ab8e24f9a1c3d5e6b \ -H "Authorization: Bearer $DNSMINT_KEY" { "released": true } A suspended or terminated hostname cannot be released: doing so would replace an enforcement record with a customer-initiated exit. Errors: 401, 403 (suspended), 404, 409 (terminated), 500. ### GET /v1/hostnames/{id}/records (list) The TXT, TLSA, CAA, MX, SRV, SSHFP, HTTPS and SVCB records under a hostname. Certificate-challenge records are not listed: they belong to issuance, not to you. curl https://dnsmint.com/api/v1/hostnames/host_01937d3e8f217c4ab8e24f9a1c3d5e6b/records \ -H "Authorization: Bearer $DNSMINT_KEY" { "records": [ { "id": "68ae1f2b3c4d5e6f708192a3", "name": "_verify.q7k4m2ab.a3f9c1-d4e7b8.dev", "type": "TXT", "ttl": 300, "data": { "text": "token-from-your-provider" } } ] } Errors: 401, 403, 404, 500. ### POST /v1/hostnames/{id}/records (add) Adds one TXT or TLSA record under the hostname. Up to 10 records per hostname, on every plan. TTL is 300 seconds and not settable. Omit `name` to put the record on the hostname itself, or give a label to put it underneath. `_acme-challenge` is reserved, because certificate issuance publishes there and overwriting it breaks renewal. Only TXT, TLSA, CAA, MX, SRV, SSHFP, HTTPS and SVCB. A hostname's address is set by PUT on the hostname, where the address class is pinned, and CAA is operator policy set by DNSMint. curl -X POST https://dnsmint.com/api/v1/hostnames/HOST_ID/records \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"type":"TXT","name":"_verify","text":"token-from-your-provider"}' HTTPS and SVCB take `priority`, `target` and `params`. Priority is 1 or above; 0 is AliasMode, which we do not serve, because it aliases a zone apex and a hostname is never an apex. A target of `.` means this name, which is the usual case. `params` are RFC 9460 service parameters as fields: `alpn` (a list, e.g. h2 or h3), `noDefaultAlpn` (only meaningful beside `alpn`), `port`, `ipv4hint` and `ipv6hint` (lists of literal addresses), `ech` (base64), and `mandatory` (the parameter names a client must understand, which may not name itself or a parameter the record does not set). Send them as fields; the ascending key order the wire format requires is ours to get right. MX takes `preference` (0-65535, lower preferred) and `exchange`, the mail host. An exchange of `.` at preference 0 is the RFC 7505 null MX: this name accepts no mail. SRV takes `priority`, `weight` and `port` (0-65535 each) and `target`. Its name must be `_._`, for example `_sip._tcp` - RFC 2782 puts them in the owner name and a client only looks there. A target of `.` says the service is decidedly not offered here. SSHFP takes `algorithm` (1 RSA, 2 DSA, 3 ECDSA, 4 Ed25519, 6 Ed448), `fptype` (1 SHA-1, 2 SHA-256) and a lowercase hex `fingerprint`. The length follows from the type: 40 digits for SHA-1, 64 for SHA-256. TXT takes `text`: a string, or a list of strings to send as-is. No control characters. Each character-string is at most 255 **bytes**. Octets, not characters, so an accented string reaches the limit sooner. A longer value is split across several, which the client concatenates. 2048 bytes in total. TLSA takes `usage` (0-3), `selector` (0-1), `matching_type` (0-2) and `association` (lowercase hex). The name must be `_._`, for example `_443._tcp` - RFC 6698 puts the port and protocol in the owner name and a client looks nowhere else. The association length has to match the matching type: 64 hex digits for SHA-256, 128 for SHA-512. TLSA returns 409 until the domain is signed and its DS record is published. A TLSA record says which certificate to trust, and in an unsigned zone anything that can spoof the answer can substitute its own binding, so the record would point clients at a certificate nobody vouched for. PUT /v1/hostnames/{id}/records/{record_id} changes a record's value in one call. Body is the same shape as the POST. Name and type stay as they are - in DNS those are what a resolver looks a record up by, so a different name or type is a different record and returns 400. Other records sharing the name are untouched. Returns 200 with the updated record, keeping the same id. Use it rather than deleting and re-adding: that costs two writes against the rate limit and leaves a window where the answer is wrong - delete-then-add has the name unresolved in between, add-then-delete transiently exceeds the ten-record cap. Errors: 400, 401, 403, 404, 409 (at the 10-record limit, hostname not live, or TLSA on an unsigned domain), 429, 500. ### PUT /v1/hostnames/{id}/records/{record_id} (change a value) Change the value of one record; name and type stay as they are, because they are what a resolver looks a record up by, so a different one is a different record. The id survives, so nothing has to track a new one. Prefer this to deleting and re-adding: that is two writes against the rate limit and leaves a window where the name resolves wrong. Body: type (required, must match the record's current type), name (optional, must resolve to the stored name), and the value fields for the type as on POST. curl -X PUT https://dnsmint.com/api/v1/hostnames/HOST_ID/records/RECORD_ID \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"type": "TXT", "text": "v=spf1 -all"}' { "id": "host_01937d3e8f217c4ab8e24f9a1c3d5e6b", "name": "q7k4m2.a3f9c1-d4e7b8.dev", "type": "TXT", "ttl": 300, "data": { "text": "v=spf1 -all" } } Errors: 400 invalid body, or a name or type that differs from the stored record; 401; 403 key lacks hostnames:write, is narrowed to another hostname, or the hostname is suspended; 404 hostname or record not found; 409 hostname is not live, or a TLSA record on an unsigned domain; 429 over the record-write limit. ### DELETE /v1/hostnames/{id}/records/{record_id} Deletes one record. 204 on success. 404 covers both "no such record" and "not yours". Errors: 401, 403, 404, 409 (hostname not live), 429, 500. ### GET /v1/hostnames/{id}/certificate For hostnames registered with `certificate: "managed"` or `certificate: "csr"`. Returns the current chain, when it expires, the mode, and which CA issued it (`ca` and `issuer`). `private_key` is present only on managed - on csr the key is yours and DNSMint never held it, so the field is absent rather than null. curl https://dnsmint.com/api/v1/hostnames/host_01937d3e8f217c4ab8e24f9a1c3d5e6b/certificate \ -H "Authorization: Bearer $DNSMINT_KEY" { "certificate": "-----BEGIN CERTIFICATE-----\n...", "private_key": "-----BEGIN PRIVATE KEY-----\n...", "mode": "managed", "names": ["q7k4m2ab.a3f9c1-d4e7b8.dev"], "expires_at": "2026-10-10T08:30:00.000Z" } Delivery is pull. DNSMint never connects to your infrastructure, so nothing is pushed to you and there is no webhook to receive. An expiring certificate is returned rather than withheld, so a working agent keeps working through a renewal problem. Only an already-expired certificate is refused, because serving that is worse than an error that says why. A managed response carries a private key and is sent with `Cache-Control: no-store, private`. Errors: 401, 403, 404 (no certificate issued yet), 409 (the hostname is `certificate: "self"`, or the certificate expired and renewal has not completed), 500. ### POST /v1/hostnames/{id}/certificate Only for hostnames registered with `certificate: "csr"`. You generate the keypair and sign a PKCS#10 request over the hostname; DNSMint places the ACME order, answers the DNS-01 challenge, and stores the chain. The private key never leaves you. Body: `csr` (required, PEM or bare base64), `wildcard` (optional, default false), `ca` (optional). curl -X POST https://dnsmint.com/api/v1/hostnames/HOST_ID/certificate \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"csr": "-----BEGIN CERTIFICATE REQUEST-----\n..."}' { "id": "host_01937d3e8f217c4ab8e24f9a1c3d5e6b", "state": "pending", "names": ["q7k4m2ab.a3f9c1-d4e7b8.dev"], "ca": "letsencrypt", "issuer": "Let's Encrypt" } Returns 202: the order is queued, not issued. Poll the GET for the chain. The CSR must carry a subjectAltName naming exactly the hostname - plus its wildcard when `wildcard` is true - and nothing else. The names come from the hostname you registered, never from the CSR; a request naming anything else is a 400, and that check is what stops a certificate being issued for a name you do not hold. It must also be signed by the private half of its own public key. ECDSA, or RSA of at least 2048 bits, with SHA-256, SHA-384 or SHA-512. Leave the subject empty; a CN is allowed but must be one of the SAN names. One order at a time per hostname. Two in flight publish four challenge values where only the two newest survive, so the older order would fail validation. Nothing renews this for you. Watch expires_at on the GET and POST a fresh request before it passes. Errors: 400 (csr missing, unreadable, unsigned by its own key, or naming anything other than this hostname), 401, 403, 404, 409 (the hostname is not certificate: "csr", or an order is already in progress), 429 (over the per-account certificate-order limit, which is lower than the other write endpoints because each accepted request spends a real certificate), 500. ### GET /v1/hostnames/{id}/diagnose (why is it not working) Answers from the authoritative side, so it reports what an external checker can only infer: whether the record is live on every nameserver, whether the zone validates from the root, whether a DNS-01 challenge was ever written and when, and whether a CAA record on the hostname is refusing the CA in use. Requires hostnames:read. Each check carries a verdict and, except on pass, an action. unknown is not pass: a log or resolver that did not answer reports unknown. A released, suspended or terminated hostname returns that as the first check and skips the rest. curl https://dnsmint.com/api/v1/hostnames/HOST_ID/diagnose \ -H "Authorization: Bearer $DNSMINT_KEY" Errors: 401, 403 key lacks hostnames:read or is narrowed to another hostname, 404, 429 too many diagnoses for this organization. ## Lifecycle rules - A hostname stays until you release it. - The record is served as soon as it is written. The status reads "pending" until every nameserver has been observed answering for it, then "live"; poll GET until then. - PUT with an unchanged address is a no-op: DNS is not rewritten. - Release (DELETE) is final for that hostname: attempts to update it return 409. The subdomain returns to your account and can be minted again as a new hostname. Nobody else is on your domain, so nobody else can receive it. - A hostname held while an abuse report is reviewed reads "suspended". It stops resolving and writes return 403, but nothing is lost: if the report is not upheld the hostname, its IP, and its DNS-01 credentials all come back. It cannot be released while suspended. - An upheld report ends in "terminated". Not reversible, and it cannot be released either, since that would replace an enforcement record with a customer-initiated exit. ## IP rules - Public IPv4 and IPv6 addresses are accepted today. IPv4 gets an A record, IPv6 an AAAA record. - Private and reserved addresses (RFC 1918 ranges, loopback, link-local, CGNAT, multicast, IPv6 ULA and v4-mapped forms) are accepted on every plan. A hostname keeps the address class it was created with: public cannot become private or the reverse, and crossing that line returns 409. A private address cannot be validated over HTTP-01, since the CA cannot reach it, so pair it with the DNS-01 API. ## Error codes Every non-2xx response is { "error": "", "code": "" }. | Status | Code | When | |--------|----------------|-------------------------------------------------------------------| | 400 | BAD_REQUEST | Bad JSON, missing "ip", invalid address, or body over 4KB | | 401 | UNAUTHORIZED | Missing or malformed Authorization header, unknown or revoked key | | 403 | FORBIDDEN | Key lacks the scope or reaches another resource, or hostname suspended | | 404 | NOT_FOUND | No hostname with this id belongs to the organization | | 409 | CONFLICT | The hostname was released or terminated and cannot change | | 429 | RATE_LIMITED | At the active-hostname cap, or past a per-endpoint write limit | | 500 | INTERNAL_ERROR | Unexpected server error | ## Serving HTTPS with Caddy On the server behind the registered IP, put the minted hostname in a Caddyfile and start Caddy. It obtains a certificate from Let's Encrypt on its own, and the hostname serves HTTPS about a minute later. q7k4m2.a3f9c1-d4e7b8.dev { reverse_proxy localhost:3000 } Any ACME client works: certbot, Traefik, lego, and cert-manager can all issue for a DNSMint hostname over HTTP-01. ## DNS-01 certificate API (acme-dns compatible) For wildcard certificates and machines on private networks, on every plan, as are private and reserved addresses. Three routes write the same challenge record and share one budget of 20 updates a minute per hostname: the API key directly (POST /v1/hostnames/{id}/acme-challenge), lego's httpreq shape (POST /httpreq/present), and the acme-dns protocol with a per-hostname credential (POST /acme/update). Take the first unless a client speaks one of the other two. ### POST /v1/hostnames/{id}/acme-credential (mint an acme-dns credential) API-key auth, dns01:write, narrowed to the hostname if the key is. The password appears in this response exactly once. At most 5 credentials per hostname, and at most 10 of these calls a minute per organization. curl -X POST https://dnsmint.com/api/v1/hostnames/HOST_ID/acme-credential \ -H "Authorization: Bearer $DNSMINT_KEY" Response (the acme-dns registration blob clients persist): { "username": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b", "password": "f3a9...", "fulldomain": "_acme-challenge.q7k4m2.a3f9c1-d4e7b8.dev", "subdomain": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b", "server_url": "https://dnsmint.com/api/acme", "allowfrom": [] } Errors: 400 credential limit reached, 401, 403 key lacks dns01:write or is narrowed to another hostname, 404, 409 hostname is not live, 429 over 10 mints a minute, 500. ### POST /acme/update (acme-dns wire protocol) Authentication is the credential, as X-Api-User and X-Api-Key headers. curl -X POST https://dnsmint.com/api/acme/update \ -H "X-Api-User: " -H "X-Api-Key: " \ -d '{"subdomain": "", "txt": "<43-char challenge>"}' Response: {"txt": "<43-char challenge>"}. The two newest values per hostname are served (covers apex plus wildcard double validation). subdomain must equal the credential's username. A credential only works while its hostname is live, so released, suspended and terminated hostnames stop accepting challenges. Errors: 401 invalid credentials, 403 subdomain mismatch, 400 malformed txt, 429 over 20 updates a minute. /update is the only acme-dns endpoint implemented. There is no /register: it is unauthenticated and carries no hostname, so there is nothing to scope a credential to. Clients that auto-register when their storage has no entry for a domain - lego, and Traefik, which vendors it - need that storage pre-seeded from POST /v1/hostnames/:id/acme-credential, keyed by hostname. Pre-seeding lego or Traefik: point both at our base URL and at a storage file, then write the acme-credential response into that file under the hostname before the first run. lego finds the entry and skips /register. ACME_DNS_API_BASE=https://dnsmint.com/api/acme ACME_DNS_STORAGE_PATH=/etc/lego/acme-dns.json # /etc/lego/acme-dns.json { "q7k4m2.a3f9c1-d4e7b8.dev": { "username": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b", "password": "f3a9...", "fulldomain": "_acme-challenge.q7k4m2.a3f9c1-d4e7b8.dev", "subdomain": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b", "server_url": "https://dnsmint.com/api/acme" } } The key is the hostname without any wildcard prefix; one entry covers the hostname and *.hostname. A client built for acme-dns reads the same JSON from its own credentials file or Secret and never calls /register. No recipe we publish needs a credential any more; every client below takes an API key. Caddy needs no credential at all. Build our module in and give it an API key carrying dns01:write: xcaddy build --with github.com/dnsmint/caddy-dnsmint *.q7k4m2.a3f9c1-d4e7b8.dev, q7k4m2.a3f9c1-d4e7b8.dev { tls { dns dnsmint {env.DNSMINT_KEY} } reverse_proxy localhost:3000 } For every site rather than one, put `acme_dns dnsmint {env.DNSMINT_KEY}` in the global options block. Source: https://github.com/dnsmint/caddy-dnsmint, built on https://github.com/dnsmint/libdns-dnsmint. lego and Traefik skip the credential too, through their built-in httpreq provider, which posts {"fqdn": "_acme-challenge..", "value": "<43 chars>"} to /api/httpreq/present and /api/httpreq/cleanup with HTTP basic auth. The password is the API key (dns01:write, narrowed to the hostname if the key is); the username can be anything but must be set, because lego sends basic auth only when both variables are: HTTPREQ_ENDPOINT=https://dnsmint.com/api/httpreq HTTPREQ_USERNAME=dnsmint HTTPREQ_PASSWORD= lego run --accept-tos --email you@example.com --dns httpreq \ -d q7k4m2.a3f9c1-d4e7b8.dev -d '*.q7k4m2.a3f9c1-d4e7b8.dev' Traefik: dnsChallenge.provider = httpreq, same three variables. RAW mode (HTTPREQ_MODE=RAW) is refused; leave HTTPREQ_MODE unset. Coolify, Dokploy and Dokku have no DNS provider of their own: the first two hand the name to Traefik, Dokku runs lego in a container, so all three take httpreq and the same three variables. Coolify: Servers > your server > Proxy > Configuration, delete the two httpchallenge command lines, add '--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=httpreq', put the variables in the traefik service's environment, Restart Proxy. Dokploy: the resolver in Settings > Web Server > Traefik, the variables in Update Traefik Environment. Dokku takes both as plugin properties: dokku letsencrypt:set --global dns-provider httpreq dokku letsencrypt:set --global dns-provider-HTTPREQ_ENDPOINT https://dnsmint.com/api/httpreq dokku letsencrypt:set --global dns-provider-HTTPREQ_USERNAME dnsmint dokku letsencrypt:set --global dns-provider-HTTPREQ_PASSWORD dokku domains:set myapp q7k4m2.a3f9c1-d4e7b8.dev dokku letsencrypt:enable myapp Mint one hostname per app on all three. The challenge names the app's domain, so that domain has to be a hostname you hold; a subdomain of one is not, because nothing is issued below a hostname. CapRover cannot do any of this: it runs certbot certonly --webroot, which is HTTP-01 only. acme.sh has a dns_dnsmint plugin taking the API key and nothing else: export DNSMINT_API_KEY= acme.sh --issue --dns dns_dnsmint \ -d q7k4m2.a3f9c1-d4e7b8.dev -d '*.q7k4m2.a3f9c1-d4e7b8.dev' The key is saved after the first run, so renewals need no environment. certbot needs no plugin. It passes CERTBOT_DOMAIN and CERTBOT_VALIDATION to a hook script, which is all the endpoints want: # /etc/letsencrypt/dnsmint-auth.sh (chmod +x; a second copy uses /cleanup) #!/bin/sh exec curl -fsS -X POST https://dnsmint.com/api/httpreq/present \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d "{\"fqdn\":\"_acme-challenge.$CERTBOT_DOMAIN.\",\"value\":\"$CERTBOT_VALIDATION\"}" certbot certonly --manual --preferred-challenges dns \ --manual-auth-hook /etc/letsencrypt/dnsmint-auth.sh \ --manual-cleanup-hook /etc/letsencrypt/dnsmint-cleanup.sh \ -d q7k4m2.a3f9c1-d4e7b8.dev -d '*.q7k4m2.a3f9c1-d4e7b8.dev' CERTBOT_DOMAIN is never the wildcard form, so a hostname and its wildcard give one challenge name and two values. Works the same from pip, a package manager, or snap. cert-manager uses our webhook solver, which runs in the cluster and reads one API key from a Secret: helm install cert-manager-webhook-dnsmint \ oci://ghcr.io/dnsmint/charts/cert-manager-webhook-dnsmint \ --namespace cert-manager solvers: - dns01: webhook: groupName: acme.dnsmint.com solverName: dnsmint config: apiKeySecretRef: name: dnsmint-api-key key: api-key Source: https://github.com/dnsmint/cert-manager-webhook-dnsmint ### POST /v1/hostnames/{id}/acme-challenge (publish with the API key) The DNS-01 challenge value, written with the API key and no credential. Same dns01:write authorization, narrowed to the hostname if the key is; same record; same two-newest rule, which covers the apex plus wildcard double validation. The hostname must be live. curl -X POST https://dnsmint.com/api/v1/hostnames/HOST_ID/acme-challenge \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"txt": "CHALLENGE_VALUE_43_CHARS"}' {"txt": "CHALLENGE_VALUE_43_CHARS"} txt is exactly 43 base64url characters. Errors: 400 invalid JSON or txt is not a 43-character value, 401, 403 key lacks dns01:write or is narrowed to another hostname, 404, 409 hostname is not live, 429 over 20 challenge updates a minute on this hostname, 500. ### DELETE /v1/hostnames/{id}/acme-challenge (withdraw) Same body. Withdrawing a value that is already gone returns 200 with "removed": false rather than an error, because a client's cleanup runs whether or not its publish completed. curl -X DELETE https://dnsmint.com/api/v1/hostnames/HOST_ID/acme-challenge \ -H "Authorization: Bearer $DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"txt": "CHALLENGE_VALUE_43_CHARS"}' {"txt": "CHALLENGE_VALUE_43_CHARS", "removed": true} Errors: 400, 401, 403, 404, 429, 500, as for POST. ### POST /httpreq/present (lego httpreq shape) What lego's built-in httpreq provider posts. Body {"fqdn", "value"} as lego sends it in its default mode; HTTP basic auth with the API key as the password (the username can be anything but must be set), or an Authorization: Bearer header. The hostname is read from the fqdn, which for a wildcard order is the base name. RAW mode (HTTPREQ_MODE=RAW, body {domain, token, keyAuth}) is refused with a 400 naming the fix. curl -X POST https://dnsmint.com/api/httpreq/present \ -u ":$DNSMINT_KEY" \ -H "Content-Type: application/json" \ -d '{"fqdn": "_acme-challenge.q7k4m2.a3f9c1-d4e7b8.dev.", "value": "CHALLENGE_VALUE_43_CHARS"}' {"fqdn": "_acme-challenge.q7k4m2.a3f9c1-d4e7b8.dev.", "value": "CHALLENGE_VALUE_43_CHARS"} Errors: 400 invalid JSON, fqdn is not a challenge name, value is not 43 characters, or RAW mode; 401; 403 key lacks dns01:write or is narrowed to another hostname; 404 no hostname by that name on this organization; 409 hostname is not live; 429 over 20 updates a minute on this hostname; 500. ### POST /httpreq/cleanup (lego httpreq shape) Same body and authentication as /present. Withdrawing a value that is already gone returns 200, because lego calls cleanup whether or not present completed. Errors as for /present, without the 409. Per-client setup (Caddy, lego, Traefik, certbot, cert-manager), one verified recipe each: https://dnsmint.com/integrations ## Kubernetes An external-dns webhook provider runs as a sidecar beside external-dns and mints hostnames from Service and Ingress annotations: https://github.com/dnsmint/external-dns-dnsmint A DNSMint hostname is an address record, so an A or AAAA endpoint becomes a hostname - minted on create, repointed on update. Other record types are published under a hostname through the records API instead. A name one label above the domain is a hostname; a name below a hostname is not, and is refused. args: ["--domain=a3f9c1-d4e7b8.dev"] # plus DNSMINT_API_KEY external-dns must run with --registry=noop --policy=upsert-only. Its default TXT registry writes ownership records beside the names it manages, at the level of the zone that holds hostnames and nothing else, so they cannot be written; without a registry it cannot tell its own names apart, and sync would let it delete names it never created. upsert-only also matches how release works: a released hostname is not revived, so deletion stays deliberate. ## Which CA you use Any of them. DNSMint's domains publish a CAA record naming Let's Encrypt and Google Trust Services, so those two work through whichever ACME client you already run and nothing else can issue for a hostname by accident. For any other CA - ZeroSSL, Buypass, an internal one - publish a CAA record on your own hostname through the records API. It replaces DNSMint's for that name, because a CA reads the closest record set to the name it is certifying and ignores everything above it. What you publish is served as written rather than merged with theirs, so a record naming only your CA excludes theirs for that hostname. Delete it and the domain policy applies again. The challenge type is not restricted, so HTTP-01 and TLS-ALPN-01 against your own IP keep working alongside DNS-01. Some CAs require External Account Binding (RFC 8555 7.3.4) and will not create an ACME account without it. Google Trust Services and ZeroSSL do; Let's Encrypt and Buypass do not. For Google, enable the Public CA API on a Google Cloud project, grant roles/publicca.externalAccountKeyCreator, and run `gcloud publicca external-account-keys create`. The HMAC it returns is base64url - decoding it as hex or standard base64 produces a binding the CA rejects as unauthorized, with nothing in the error to say the encoding was the problem. certbot takes --eab-kid and --eab-hmac-key; Caddy takes an external_account block; acme.sh takes both on --register-account. For certificate: "managed" and certificate: "csr" - the modes where DNSMint runs the ACME client - the "ca" field picks the issuer: "letsencrypt" (default) or "google". Full reference at https://dnsmint.com/ca ## MCP endpoint DNSMint runs an MCP server at https://dnsmint.com/mcp over the Streamable HTTP transport. It is POST-only and stateless; a GET returns 405, which the transport spec allows for a server that never initiates anything. Two credentials are accepted, and only at this endpoint: - A DNSMint API key as a bearer token. This is how Claude Code, Cursor and VS Code connect, since all three let you set an Authorization header: claude mcp add --transport http dnsmint https://dnsmint.com/mcp \ --header "Authorization: Bearer $DNSMINT_KEY" - An OAuth 2.1 access token. This is the only way Claude's and ChatGPT's connectors can authenticate, because they take a URL and nothing else. Add the URL as a connector and the client runs the flow itself: it registers through RFC 7591 dynamic client registration, the person signs in, chooses what the connector may do and can hold it to a single domain, and the resulting token is bound to this endpoint and refused anywhere else. An access token is not an API key and will not authenticate against /api/v1. - On a machine with no browser, the device flow (RFC 8628). POST client_id to https://dnsmint.com/api/oauth/device_authorization and it returns a user_code and a device_code. Show the user_code; a person opens https://dnsmint.com/device anywhere else, types it and approves. Poll https://dnsmint.com/api/oauth/token with grant_type=urn:ietf:params:oauth:grant-type:device_code and the device_code. Polling answers 400 authorization_pending until approval, 400 slow_down if polled faster than the returned interval, then 200 with tokens. Nothing is decided on the machine and no long-lived secret is copied onto it. Discovery is at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server, and an unauthenticated call returns 401 with a WWW-Authenticate header naming the first of those. The tools are not listed here. A client calls tools/list and receives names, argument schemas and descriptions from the server, which is always current; what a connector is offered depends on what it was granted, so a read-only connection is not shown minting or releasing at all. Releasing a hostname requires the full hostname passed as a confirmation argument, because a release is permanent and tombstoned. Connected applications are listed on the API keys page in the dashboard, and disconnecting one revokes its tokens so it stops on its next call. ## Agent standards DNSMint provides a hostname and a certificate rail. It does not issue agent identities and does not run a naming registry. The standards below sit above that and need nothing from DNSMint beyond a hostname that resolves and a certificate that matches it. A2A (Agent2Agent, Linux Foundation). Discovery starts with an Agent Card served over HTTPS. Since A2A v1.0 the path is /.well-known/agent-card.json; the pre-0.3 path /.well-known/agent.json is still checked by many deployed clients, so serving both is common during the transition. A minted hostname is the origin that card is served from. Repointing the hostname with PUT moves the agent without invalidating a cached card, because the name did not change. SPIFFE and SPIRE. SPIRE attaches a DNS name to an X509-SVID via `spire-server entry create -dns `, which places the name in the certificate it issues. A workload can hold a SPIFFE ID and a minted hostname simultaneously with no coordination: SPIFFE says which workload it is inside a trust domain, the hostname is how anything outside that trust domain reaches it. DNSMint is not a SPIFFE trust domain and does not issue SVIDs. ANS (Agent Name Service). ANS resolves an agent name to an endpoint. A minted hostname is an endpoint an ANS registration can name today. Registration is between the operator and the registry; DNSMint is what the registration points at. The ANS v2 IETF draft anchors names to DNS and expects records published under the agent hostname, including TLSA. Those are the record types DNSMint serves: A, AAAA, TXT, CAA and TLSA. The records API publishes them under any hostname you hold, TLSA included: every domain is signed and its DS published before it serves a hostname, and a TLSA record only carries weight in a signed zone. DNSMint tracks the draft and ships against it as it lands. ## Pricing Billing is month to month; cancel any time. DNS queries and API reads are unlimited on every plan, under a fair use policy covering attacks and abuse only; hostname updates carry a technical rate limit, stated in the API reference. There are two plans. Starter is free for an introductory period and then billed monthly: a small number of hostnames on a domain of your own. Everything else is the Standard plan, which you build. Choose how many dedicated domains and how many hostnames you need; the price follows and is shown before you confirm. Current prices and limits are on the pricing page, which is the only place they are stated. Every feature is on every plan - custom subdomains, wildcard certificates, the DNS-01 certificate API, and public or private addresses. Plans differ only by how much, never by what. No account shares a domain with another, on any plan. Let's Encrypt caps new certificates per registered domain, so that cap is yours alone rather than shared with strangers, and so are the cookie origin and the reputation. A fleet large enough to reach it needs a second domain, which is why the number of domains is something you choose. A card is required at signup on every plan. Pricing page: https://dnsmint.com/pricing ## Fair use summary Unlimited means DNS queries, API reads, and hostname updates are not metered or billed, however heavy the workload. We may throttle or apply a limit where it is needed to keep the service available for everyone. The fair use policy covers attacks and platform abuse: denial of service against the nameservers, using the API as a storage or exfiltration channel, evading rate limits across accounts, and hosting phishing or malware. Hard limits: registration writes carry per-key rate limits (burst plus sustained), and each tier has a published cap on active hostnames, on the pricing page. Where the problem is load, enforcement follows a ladder: automated mitigation, then notice, then suspension. Conduct does not get the ladder: where we believe on reasonable grounds that an account is being used for what section 3 of the terms prohibits, we suspend it immediately and investigate afterwards, and phishing and malware hostnames are taken down as soon as we see them. Suspension is reversible and we say why. Full policy: https://dnsmint.com/fair-use. Report abuse: hello@dnsmint.com. ## FAQ Q: How fast is a new hostname usable? A: Seconds for DNS, about a minute for HTTPS. The API reports the record as live once the nameservers holding its zone answer for it, and the certificate authority usually finishes right after. Q: What happens when my server's IP changes? A: Send one PUT with the new address. The hostname and its certificate carry over untouched, so everything pointing at the hostname keeps working. Q: Who sees my traffic? A: You and your users. DNSMint's role ends at the DNS answer; connections run directly to your server, on your bandwidth, and nothing routes through us to inspect. That does not change with managed certificates: we keep a copy of the key so you can fetch it, and there is still no traffic here to read. Q: Which certificate authorities work? A: Any ACME certificate authority, when you run the client. Our domains name Let's Encrypt and Google Trust Services in a CAA record, so those two work straight away; for ZeroSSL, Buypass or anything else, publish a CAA record on your hostname naming it and that applies instead of ours. Either way your private key never leaves your server. With managed certificates we run the client for you, against Let's Encrypt or Google Trust Services as you choose - you get the certificate and its key, and we keep a copy so you can fetch the current pair any time without us pushing anything at you. Every renewal generates a fresh keypair, so a managed key is never years old. Q: How long do hostnames last? A: Until you release it. Releasing stops DNS and revokes the certificate, and you can mint the same hostname again afterwards. The one thing with a clock on it is the Starter domain: it is registered for a year and not renewed. We tell you before it lapses, and you claim a replacement, which is a different name; hostnames do not move between the two. Q: What do I need to bring? A: A server with an IP address, public or private, and a card on file. The domain, the DNS, and the certificate plumbing are included, and the domain is yours alone on every plan, so the Let's Encrypt rate limits are yours alone too. ## Links - Site: https://dnsmint.com - Quickstart: https://dnsmint.com/quickstart - Features: https://dnsmint.com/features - API reference: https://dnsmint.com/api-reference - MCP endpoint: https://dnsmint.com/mcp - OpenAPI spec: https://dnsmint.com/openapi.json - Pricing: https://dnsmint.com/pricing - Fair use: https://dnsmint.com/fair-use - Questions: https://dnsmint.com/questions - Use cases: https://dnsmint.com/use-cases - Agent-spawned compute: https://dnsmint.com/use-cases/agent-compute - MCP servers: https://dnsmint.com/use-cases/mcp-servers - A2A agents: https://dnsmint.com/use-cases/a2a-agents - For agent platforms: https://dnsmint.com/agent-builders - Comparisons: https://dnsmint.com/vs - Magic DNS (nip.io, sslip.io): https://dnsmint.com/vs/sslip-io - Dynamic DNS (DuckDNS, No-IP): https://dnsmint.com/vs/duckdns - Programmable DNS and DIY: https://dnsmint.com/vs/dns-api - A private CA you ship: https://dnsmint.com/vs/private-ca - Tunnels (ngrok, Cloudflare Tunnel): https://dnsmint.com/vs/tunnels - Terms: https://dnsmint.com/terms - Privacy: https://dnsmint.com/privacy - Contact: hello@dnsmint.com - Report abuse: hello@dnsmint.com