API reference

The base URL is https://dnsmint.com/api/v1. Requests and responses are JSON, and request bodies are capped at 4KB. The quickstart walks the whole loop from key to certificate.

Authentication

Every endpoint requires an API key in the Authorization header:

Authorization: Bearer dnsm_<key_id>_<secret>

Keys are created in the dashboard, which also lists and revokes them. The secret appears once, in the creation response, and is stored only as a hash after that. A missing, malformed, or revoked key gets:

{ "error": "Missing or invalid API key", "code": "UNAUTHORIZED" }

A key carries scopes - hostnames:read, hostnames:write and dns01:write - and each scope carries what it reaches: the whole team, one domain, or one hostname. A key that lacks the scope an endpoint requires gets 403, and so does one narrowed away from the hostname in the request:

{ "error": "This API key's \"dns01:write\" scope is limited to q7k4m2.example.dev", "code": "FORBIDDEN" }

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 it, including ones minted later, and reaches nothing else on the account. That is the shape for a deploy pipeline or a cluster on an 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 it cannot mint a credential for anything else you run, repoint an IP, or release a name. A key created without naming a scope gets all three on the whole team.

A key can also be given a lifetime when it is created. An expired key stops authenticating and says so, naming the date, so it is never confused with a malformed or revoked one - 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 you are trialling - so the credential retires with the thing it was minted for. Keys created without a lifetime do not expire, which is the right answer for something long-running, and is what every key created before this existed still does.

Errors

Every non-2xx response has the same shape: an error string for humans and a stable code for programs.

{ "error": "Hostname not found", "code": "NOT_FOUND" }
StatusCodeWhen
400BAD_REQUESTThe body is not valid JSON, is over 4KB, is missing "ip", or the address does not parse
401UNAUTHORIZEDThe Authorization header is missing, malformed, or the key is unknown or revoked
402PAYMENT_REQUIREDSignup is unfinished: no card on file, so no domain has been assigned yet
403FORBIDDENThe API key does not carry the scope this call needs, or its grant does not reach the hostname being minted
409CONFLICTThe requested subdomain is taken or retired on this domain
409DOMAIN_PROVISIONINGA domain for this team is still being registered. Carries Retry-After and a Location pointing at the provisioning status
409NO_DOMAIN_AVAILABLENo domain is assigned to this team and none is being registered. Ours to fix; a timed retry will not help
429RATE_LIMITEDThe account is at its active-hostname cap, or has made more than 30 registrations in a minute
500INTERNAL_ERRORUnexpected server error
501NOT_IMPLEMENTEDcertificate: "managed" was requested and this deployment has no certificate encryption key configured

Hostname status

Every hostname response carries a status. These are the only values it takes.

  • pending - registered, not yet confirmed on the nameservers. Poll GET until it goes live.
  • live - served.
  • released - you released it. It stops appearing in listings and writes to it return 409. The subdomain is yours to mint again, as a new hostname.
  • suspended - held while an abuse report is reviewed. 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.
  • terminated - an abuse report was upheld. Not reversible, and it stops appearing in listings.

POST /api/v1/hostnames

Registers an IP address and mints a hostname. The subdomain is opaque unless you pass one, and lands on a domain assigned to your account.

Parameters

  • ip (body, required) An IPv4 or IPv6 address as a string. The record type follows the address: A for IPv4, AAAA for IPv6. Public, private, and reserved ranges are all accepted, on every plan. The address you mint with fixes the hostname's address class for its lifetime - see Address classes.
  • subdomain (body, optional) A custom subdomain, on every plan. 3-63 lowercase letters, digits, and hyphens; no periods; no leading or trailing hyphen.
  • domain (body, optional) Which of your dedicated domains to mint on. Must already be assigned to the team. Omitted, we pick one.
  • certificate (body, optional) Who holds the private key, and with it who runs the ACME client and who talks to the CA. self (default) means you do all three: run an ACME client against the DNS-01 API, choose your own CA, and we never see key material. managed means we do all three and renew before expiry on a fresh key each time, and you pull the current pair from GET /v1/hostnames/{id}/certificate. See Certificate modes.
  • ca (body, optional) Which certificate authority issues, for certificate: "managed" and certificate: "csr", the modes where we run the ACME client - 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, we pick Let's Encrypt unless it has recently rate-limited orders for your domain. Renewals always return to the CA that issued. See https://dnsmint.com/ca
$ 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"}'

201 with the new hostname:

{
  "id": "68ad3a1e9c4b2f0d5e6a7b8c",
  "hostname": "q7k4m2.dnsmint-a3f9c1.dev",
  "status": "pending",
  "certificate": "self",
  "created_at": "2026-08-26T08:30:00.000Z"
}

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.

Any account may pass subdomain, and may pass domain, which must be one already assigned to it; domain is only a choice once the team holds more than one.

Every plan has its own cap on active hostnames. Registration past the cap returns 429, as does more than 30 registrations a minute.

StatusCodeWhen
400BAD_REQUESTThe body is not valid JSON, is over 4KB, is missing "ip", or the address does not parse
401UNAUTHORIZEDThe Authorization header is missing, malformed, or the key is unknown or revoked
402PAYMENT_REQUIREDSignup is unfinished: no card on file, so no domain has been assigned yet
403FORBIDDENThe API key does not carry the scope this call needs, or its grant does not reach the hostname being minted
409CONFLICTThe requested subdomain is taken or retired on this domain
409DOMAIN_PROVISIONINGA domain for this team is still being registered. Carries Retry-After and a Location pointing at the provisioning status
409NO_DOMAIN_AVAILABLENo domain is assigned to this team and none is being registered. Ours to fix; a timed retry will not help
429RATE_LIMITEDThe account is at its active-hostname cap, or has made more than 30 registrations in a minute
500INTERNAL_ERRORUnexpected server error
501NOT_IMPLEMENTEDcertificate: "managed" was requested and this deployment has no certificate encryption key configured

GET /api/v1/hostnames

Lists the team's hostnames, newest first. Released and terminated hostnames are excluded; suspended ones are shown, so a team can see what has been actioned.

Parameters

  • limit (query, optional) Maximum results to return. Default 100; values are clamped to the 1 to 500 range.
  • skip (query, optional) Results to skip, for pagination. Default 0.
$ curl "https://dnsmint.com/api/v1/hostnames?limit=100&skip=0" \
    -H "Authorization: Bearer $DNSMINT_KEY"

200 with a hostnames envelope:

{
  "hostnames": [
    {
      "id": "68ad3a1e9c4b2f0d5e6a7b8c",
      "hostname": "q7k4m2.dnsmint-a3f9c1.dev",
      "status": "live",
      "certificate": "self",
      "created_at": "2026-08-26T08:30:00.000Z"
    }
  ],
  "total": 1,
  "active": 1,
  "cap": 5,
  "limit": 100,
  "skip": 0
}

Released and terminated hostnames never appear.

total is the non-released count. active is live hostnames, which is what the create cap counts. cap is the plan's maxActiveHostnames. limit and skip are the values actually applied after clamping.

Another page exists when skip + limit < total.

StatusCodeWhen
401UNAUTHORIZEDThe Authorization header is missing, malformed, or the key is unknown or revoked
500INTERNAL_ERRORUnexpected server error

GET /api/v1/hostnames/{id}

Reads one hostname by id.

Parameters

  • id (path, required) The hostname id returned at creation.
$ curl https://dnsmint.com/api/v1/hostnames/68ad3a1e9c4b2f0d5e6a7b8c \
    -H "Authorization: Bearer $DNSMINT_KEY"

200 with the hostname:

{
  "id": "68ad3a1e9c4b2f0d5e6a7b8c",
  "hostname": "q7k4m2.dnsmint-a3f9c1.dev",
  "status": "live",
  "certificate": "self",
  "created_at": "2026-08-26T08:30:00.000Z"
}

New registrations may briefly return "pending"; poll GET until "live".

StatusCodeWhen
401UNAUTHORIZEDThe Authorization header is missing, malformed, or the key is unknown or revoked
404NOT_FOUNDNo hostname with this id belongs to the authenticated account
500INTERNAL_ERRORUnexpected server error

PUT /api/v1/hostnames/{id}

Sets the hostname's IP. The same address is a no-op; a new address updates the record. The name and its certificates carry over untouched.

Parameters

  • id (path, required) The hostname id returned at creation.
  • ip (body, instead of target) The IPv4 or IPv6 address, public or private. Repeating the current address is a no-op; nothing needs to be sent to keep a hostname live. A different address updates the record; the record type follows the address, so a hostname can move between A and AAAA. It cannot move between address classes - a public hostname stays public and a private one stays private, and crossing that line returns 409. See Address classes.
  • target (body, instead of ip) A platform endpoint to follow instead of an address. Send this or ip, never both. We resolve it and publish ordinary A and AAAA records, refreshed within the target's own TTL, so the name never carries a CNAME and every record you set on the hostname keeps working. The 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. Setting ip takes the hostname back off its target.
$ curl -X PUT https://dnsmint.com/api/v1/hostnames/68ad3a1e9c4b2f0d5e6a7b8c \
    -H "Authorization: Bearer $DNSMINT_KEY" \
    -H "Content-Type: application/json" \
    -d '{"ip": "34.120.51.22"}'

200 with the updated hostname:

{
  "id": "68ad3a1e9c4b2f0d5e6a7b8c",
  "hostname": "q7k4m2.dnsmint-a3f9c1.dev",
  "status": "pending",
  "certificate": "self",
  "created_at": "2026-08-26T08:30:00.000Z"
}

If the address is unchanged, DNS is not rewritten and nothing changes. There is no keepalive to send: a hostname stays live without being touched.

If the address changed, the record is replaced and the hostname comes back live.

A released or terminated hostname cannot be updated; that returns 409. A suspended one returns 403 until the review is resolved.

Repointing across address classes returns 409. A hostname minted on a public address can never be moved to a private one, or the reverse; release it and mint a new hostname instead. See Address classes for why.

More than 60 updates a minute on one team returns 429.

StatusCodeWhen
400BAD_REQUESTInvalid JSON, missing "ip" field, syntactically invalid IP address, or body over 4KB
401UNAUTHORIZEDThe Authorization header is missing, malformed, or the key is unknown or revoked
403FORBIDDENThe hostname is suspended pending an abuse review, or the API key does not carry the scope this call needs, or its grant does not reach this hostname
404NOT_FOUNDNo hostname with this id belongs to the authenticated account
409CONFLICTEither the hostname was released or terminated, neither of which can be updated, or the new address is a different class from the one the hostname was minted with: a public hostname cannot become private, or the reverse
429RATE_LIMITEDMore than 60 updates in a minute in this team
500INTERNAL_ERRORUnexpected server error

DELETE /api/v1/hostnames/{id}

Releases the hostname. The record stops being served and any certificate we hold is revoked. The subdomain returns to your account and can be minted again.

Parameters

  • id (path, required) The hostname id returned at creation.
$ curl -X DELETE https://dnsmint.com/api/v1/hostnames/68ad3a1e9c4b2f0d5e6a7b8c \
    -H "Authorization: Bearer $DNSMINT_KEY"

200 with a confirmation:

{
  "released": true
}

The DNS record stops being served at once. The released hostname stays released: writes to it return 409, and minting the subdomain again creates a new hostname. Releasing an already released hostname returns the same 200.

A suspended or terminated hostname cannot be released. Releasing one would replace an enforcement record with a customer-initiated exit, so it returns 403 or 409 instead.

StatusCodeWhen
401UNAUTHORIZEDThe Authorization header is missing, malformed, or the key is unknown or revoked
403FORBIDDENThe hostname is suspended pending an abuse review and cannot be released
404NOT_FOUNDNo hostname with this id belongs to the authenticated account
409CONFLICTThe hostname was terminated for policy violation and cannot be released
500INTERNAL_ERRORUnexpected server error

GET /api/v1/hostnames/:id/diagnose

Answers "why is this not working" from the authoritative side. We are the nameserver for this name, so this reports things an external checker can only infer: whether the record is live on every node of ours, whether the zone validates from the root, whether a DNS-01 challenge was ever written and when, and whether a CAA record on your hostname is quietly refusing the CA you are trying to use.

Parameters

  • id (path) The hostname id returned when it was minted.
$ curl https://dnsmint.com/api/v1/hostnames/HOST_ID/diagnose \
    -H "Authorization: Bearer $DNSMINT_KEY"
{
  "hostname": "q7k4m2.dnsmint-a3f9c1.dev",
  "summary": "q7k4m2.dnsmint-a3f9c1.dev is not working: no unexpired certificate covering it in crt.sh",
  "checks": [
    { "id": "record", "label": "record", "verdict": "pass", "detail": "A 203.0.113.10, live, TTL 60" },
    { "id": "nameservers", "label": "nameservers", "verdict": "pass", "detail": "ns1 12ms, ns2 31ms" },
    { "id": "dnssec", "label": "DNSSEC", "verdict": "pass", "detail": "chain validates from the root (cloudflare set AD)" },
    {
      "id": "certificate",
      "label": "certificate",
      "verdict": "fail",
      "detail": "no unexpired certificate covering q7k4m2.dnsmint-a3f9c1.dev in crt.sh",
      "action": "Your ACME client has not completed a challenge. The DNS-01 line above says whether one was ever written."
    }
  ],
  "checked_at": "2026-09-05T09:14:02.000Z"
}

unknown is not pass. A certificate transparency log that did not answer, or a resolver we could not reach, reports unknown - reading that as healthy is how a real fault gets closed as fine.

action is present on everything except pass, and absent when we cannot honestly name a next step.

A hostname that is released, suspended or terminated returns that as the first check and skips the rest, because every one of them would fail for the same reason.

The checks run cheapest first and share a time budget. One that ran out of time comes back unknown rather than delaying the response.

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks hostnames:read, or is narrowed to another hostname
404NOT_FOUNDHostname not found
429RATE_LIMITEDEach call queries both nameservers, two public resolvers and a certificate transparency log

Certificate modes

The certificate field on a hostname decides one thing: who holds the private key. That choice carries two others with it - who runs the ACME client, and who talks to the certificate authority.

ModePrivate keyACME clientTalks to the CARenewal
selfYouYouYouYours
csrYouUsUsYours
managedUsUsUsOurs, automatic

self is the default, and it is the only mode where we never see key material. You run certbot, lego, acme.sh or anything else that speaks acme-dns, point it at our DNS-01 API, and choose your own CA. We publish the challenge record and nothing else.

managed exists for the case where running an ACME client is not on the table. We generate the keypair, order the certificate, answer the challenge in our own zone, and renew it before expiry - every renewal on a fresh key. You fetch the current pair from GET /v1/hostnames/{id}/certificate whenever you want it. Delivery is pull: we never connect to your infrastructure.

csr sits between them, and the row above is the honest way to read it: you keep the key, we run the protocol. Register the hostname, then sign a PKCS#10 request over the name and POST /v1/hostnames/{id}/certificate. We place the order, answer the challenge, and store the chain for you to fetch. The private key never leaves you, so there is nothing of yours for us to lose.

On csr, renewal is yours. That is not an omission. We hold no private key for the hostname, so we cannot build the replacement request, and the two ways round it are both worse: reusing your original request forever would pin one keypair to the name for its whole life, and calling out to you for a new one would mean connecting to your infrastructure, which we do not do. So watch expires_at and post a fresh request before it passes. First issuance and every renewal are the same call.

You cannot send a CSR when you register, because the hostname it has to name is minted by that call. Register with certificate: "csr", read the hostname off the response, then post the request.

All three work for a machine nothing can reach inbound, which is the point of DNS-01. The difference is custody, not capability.

Address classes

Every address is either public, routable on the internet, or private - RFC 1918 space, loopback, link-local, unique local, and the other reserved ranges. Both are accepted on every plan. A hostname pointing at 10.0.0.5 is the normal case for a service inside a VPC or on a home network, and DNS-01 exists so that a machine nothing can reach inbound can still hold a publicly trusted certificate.

A hostname keeps the address class it was minted with, for its whole life. You can repoint it freely within that class - one public address to another, or one private address to another, including between A and AAAA. Crossing from public to private, or private to public, returns 409 CONFLICT. Release the hostname and mint a new one for the other class.

This is a deliberate limit, and the reason is DNS rebinding. Without it, someone could mint a hostname on a public address, obtain a real certificate for it, lure a browser to https://that-name, and then repoint the same name at 127.0.0.1 or an address inside the visitor's network. The origin string never changes, so the browser still treats the attacker's scripts as belonging there, and they can read responses from whatever is listening on that machine or LAN - a router page, a dashboard, a local model endpoint. The victim is someone who never used DNSMint at all, and a valid certificate is what would make it work without a warning.

The attack needs a single origin that is public first and private second. Pinning the class means no one hostname can be both, and a second hostname is a second origin, which the same-origin policy already stops. Publishing a private address is not the danger and is not restricted - moving a name across the boundary is.

Records under a hostname

TXT, TLSA, CAA, MX, SRV, SSHFP, HTTPS and SVCB records you publish yourself, on every plan. Ten per hostname. A TXT value longer than one character-string is split across several, which is what carries a 2048-bit DKIM key.

GET /api/v1/hostnames/:id/records

The TXT, TLSA, CAA, MX, SRV, SSHFP, HTTPS or SVCB records under a hostname. Certificate-challenge records are not listed: they belong to issuance, not to you.

Parameters

  • id (path) The hostname id returned when it was minted.
$ curl https://dnsmint.com/api/v1/hostnames/HOST_ID/records \
    -H "Authorization: Bearer $DNSMINT_KEY"
{
  "records": [
    {
      "id": "6a1f...",
      "name": "_verify.q7k4m2.dnsmint-a3f9c1.dev",
      "type": "TXT",
      "ttl": 300,
      "data": { "text": "token-from-your-provider" }
    }
  ]
}
StatusCodeWhen
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks hostnames:read, or is narrowed to another hostname
404NOT_FOUNDHostname not found

POST /api/v1/hostnames/:id/records

TXT, TLSA, CAA, MX, SRV, SSHFP, HTTPS or SVCB records under a hostname you hold. Up to 10 per hostname, on every plan. TTL is 300s and not settable. To change a value, PUT the record's id rather than deleting and re-adding. Omit name to put the record on the hostname itself. _acme-challenge is reserved - certificate issuance publishes there.

Parameters

  • id (path) The hostname id returned when it was minted.
  • type (body, required) TXT, TLSA, CAA, MX, SRV, SSHFP, HTTPS or SVCB. A hostname's address is set by PUT on the hostname itself, so A and AAAA are not accepted here.
  • name (body, optional) Label under the hostname. Omit for a record on the hostname itself. _acme-challenge is reserved: certificate issuance publishes there. A TLSA record must be named for its port and protocol, for example _443._tcp - RFC 6698 puts them in the owner name, so a client only looks there.
  • text (body, TXT only) A string, or a list of strings to send as-is. Each character-string is at most 255 bytes - octets rather than characters, so a non-ASCII value reaches the limit sooner - and a longer value is split across several, which the client concatenates. 2048 bytes in total. No control characters.
  • usage (body, TLSA only) TLSA certificate usage, 0 to 3. 3 is DANE-EE.
  • selector (body, TLSA only) TLSA selector. 0 full certificate, 1 SubjectPublicKeyInfo.
  • matching_type (body, TLSA only) TLSA matching type. 0 full, 1 SHA-256, 2 SHA-512. The association length must match: 64 hex digits for SHA-256, 128 for SHA-512.
  • association (body, TLSA only) TLSA certificate-association data, lowercase hex, no separators.
  • preference (body, MX only) Lower is preferred. 0 with an exchange of . is the RFC 7505 null MX, which says this name accepts no mail.
  • exchange (body, MX only) The mail host. . is the root, for the null MX.
  • priority (body, SRV only) Lower is tried first.
  • weight (body, SRV only) Relative share among targets of equal priority.
  • port (body, SRV only) The port the service listens on.
  • target (body, SRV only) The host offering the service. . says the service is decidedly not offered at this name.
  • algorithm (body, SSHFP only) 1 RSA, 2 DSA, 3 ECDSA, 4 Ed25519, 6 Ed448.
  • fptype (body, SSHFP only) 1 for SHA-1, 2 for SHA-256. The fingerprint length follows from it.
  • fingerprint (body, SSHFP only) Lowercase hex, no separators. 40 digits for SHA-1, 64 for SHA-256.
  • params (body, HTTPS and SVCB only) RFC 9460 service parameters as fields: alpn, noDefaultAlpn, port, ipv4hint, ipv6hint, ech, mandatory. We emit them in the ascending key order the wire format requires.
$ 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"}'
{
  "id": "6a1f...",
  "name": "_verify.q7k4m2.dnsmint-a3f9c1.dev",
  "type": "TXT",
  "ttl": 300,
  "data": { "text": "token-from-your-provider" }
}

A hostname’s address is set by PUT on the hostname, so it is not accepted here. TLSA returns 409 until the domain is signed and its DS is published: an unsigned TLSA record is a certificate binding nobody can verify.

CAA replaces the domain's policy for that hostname rather than adding to it. A CA reads the nearest ancestor with a CAA record set and ignores the rest, so publishing one here takes over which CAs may issue for your name - including excluding ours. Delete it and the domain's policy applies again. That is how CAA works and we serve what you write; we do not merge it with ours.

StatusCodeWhen
400BAD_REQUESTUnusable type, name or rdata
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks hostnames:write, or is narrowed to another hostname
404NOT_FOUNDHostname not found
409CONFLICTAt the 10-record limit, the hostname is frozen, or TLSA on an unsigned zone
429RATE_LIMITEDRate limited

PUT /api/v1/hostnames/:id/records/:record_id

Change the value of one record. The id in the path says which; the body says what it should now be. Name and type stay as they are.

Parameters

  • id (path) The hostname id.
  • record_id (path) The record id, from the list or create response. This is what says which record changes; the name does not.
  • type (body, required) TXT, TLSA, CAA, MX, SRV, SSHFP, HTTPS or SVCB, and it must match the record's current type. A different type is a different record, so POST it.
  • name (body, optional) Label under the hostname, omitted for a record on the hostname itself. Must resolve to the stored record's name; changing it returns 400.
  • text (body, TXT only) One string, at most 255 bytes, no control characters. Bytes rather than characters: a non-ASCII string encodes to more bytes than it has characters.
  • usage (body, TLSA only) TLSA certificate usage, 0 to 3. 3 is DANE-EE.
  • selector (body, TLSA only) TLSA selector. 0 full certificate, 1 SubjectPublicKeyInfo.
  • matching_type (body, TLSA only) TLSA matching type. 0 full, 1 SHA-256, 2 SHA-512. The association length must match: 64 hex digits for SHA-256, 128 for SHA-512.
  • association (body, TLSA only) TLSA certificate-association data, lowercase hex, no separators.
  • preference (body, MX only) Lower is preferred. 0 with an exchange of . is the RFC 7505 null MX, which says this name accepts no mail.
  • exchange (body, MX only) The mail host. . is the root, for the null MX.
  • priority (body, SRV only) Lower is tried first.
  • weight (body, SRV only) Relative share among targets of equal priority.
  • port (body, SRV only) The port the service listens on.
  • target (body, SRV only) The host offering the service. . says the service is decidedly not offered at this name.
  • algorithm (body, SSHFP only) 1 RSA, 2 DSA, 3 ECDSA, 4 Ed25519, 6 Ed448.
  • fptype (body, SSHFP only) 1 for SHA-1, 2 for SHA-256. The fingerprint length follows from it.
  • fingerprint (body, SSHFP only) Lowercase hex, no separators. 40 digits for SHA-1, 64 for SHA-256.
  • params (body, HTTPS and SVCB only) RFC 9460 service parameters as fields: alpn, noDefaultAlpn, port, ipv4hint, ipv6hint, ech, mandatory. We emit them in the ascending key order the wire format requires.
$ 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"}'

200 with the updated record:

{
  "id": "68ad3a1e9c4b2f0d5e6a7b8c",
  "name": "q7k4m2.dnsmint-a3f9c1.dev",
  "type": "TXT",
  "ttl": 300,
  "data": { "text": "v=spf1 -all" }
}

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 it leaves a window where the name resolves wrong or the ten-record cap is briefly exceeded.

Name and type cannot change. They are what a resolver looks a record up by; a different one is a different record.

Other records sharing this one's name are untouched - the id says which record, not the name.

TTL is 300s and not settable, the same as on create.

StatusCodeWhen
400BAD_REQUESTInvalid rdata, or the body names a different name or type than the record holds - that is a different record, so POST it
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks hostnames:write, is narrowed to another hostname, or the hostname is suspended
404NOT_FOUNDNo such hostname, or no such record under it - the same answer either way, so a record id from another team cannot be confirmed
409CONFLICTThe hostname is not live, or the record is TLSA and the domain is not signed
429RATE_LIMITEDOver recordWritePerMinute for this team

DELETE /api/v1/hostnames/:id/records/:record_id

204 on success. 404 covers both “no such record” and “not yours”: the ownership filter is the authorisation, and telling the two apart would confirm another team’s record id.

Parameters

  • id (path) The hostname id.
  • record_id (path) The record id returned when it was created or listed.
$ curl -X DELETE https://dnsmint.com/api/v1/hostnames/HOST_ID/records/RECORD_ID \
    -H "Authorization: Bearer $DNSMINT_KEY"
StatusCodeWhen
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks hostnames:write, or is narrowed to another hostname
404NOT_FOUNDHostname or record not found
409CONFLICTHostname is frozen
429RATE_LIMITEDRate limited

Managed certificates

Register a hostname with certificate: "managed" and we run ACME for it, renew it, and keep a copy so you can fetch the current pair. Or keep the default, certificate: "self", and your key never leaves your server.

GET /api/v1/hostnames/:id/certificate

The current certificate for a hostname we run ACME for. On certificate: "managed" that includes the private key, because we generated it and keep a copy. On certificate: "csr" it does not, because you hold it - the chain and its expiry are all we have.

Parameters

  • id (path) The hostname id returned when it was minted.
$ curl https://dnsmint.com/api/v1/hostnames/HOST_ID/certificate \
    -H "Authorization: Bearer $DNSMINT_KEY"
{
  "certificate": "-----BEGIN CERTIFICATE-----\n...",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...",
  "names": ["q7k4m2.dnsmint-a3f9c1.dev"],
  "expires_at": "2026-10-10T08:30:00.000Z",
  "mode": "managed",
  "ca": "letsencrypt",
  "issuer": "Let's Encrypt"
}

private_key is present only for managed. Its absence on csr is the accurate statement: we never held that key.

mode echoes the hostname's certificate mode, so a client can tell which shape it is looking at without a second call.

On csr, watch expires_at and POST a fresh request before it passes. Nothing renews it for you.

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks hostnames:read, or is narrowed to another hostname
404NOT_FOUNDHostname not found, or nothing issued for it yet
409CONFLICTThe hostname is certificate: "self", or the certificate has expired and renewal has not completed

POST /api/v1/hostnames/:id/certificate

For hostnames registered with certificate: "csr": you keep the private key, we run the ACME protocol around the request you send. This is how first issuance and every renewal happen - they are the same call.

Parameters

  • id (path) The hostname id returned when it was minted.
  • csr (body, required) A PKCS#10 certificate request, PEM or bare base64. It must carry a subjectAltName naming exactly this hostname - plus its wildcard when you pass wildcard - and must 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.
  • wildcard (body, optional) Cover *.hostname as well as the hostname. Default false. The csr must name both when this is true.
  • ca (body, optional) Which authority issues - letsencrypt or google. Omitted, we pick. See https://dnsmint.com/ca
$ openssl req -new -newkey ec:<(openssl ecparam -name prime256v1) \
    -nodes -keyout key.pem -subj "/" \
    -addext "subjectAltName=DNS:$HOSTNAME" -out req.pem

curl -X POST https://dnsmint.com/api/v1/hostnames/HOST_ID/certificate \
    -H "Authorization: Bearer $DNSMINT_KEY" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --rawfile csr req.pem '{csr: $csr}')"

202 with the queued order:

{
  "id": "68ad3a1e9c4b2f0d5e6a7b8c",
  "state": "pending",
  "names": ["q7k4m2.dnsmint-a3f9c1.dev"],
  "ca": "letsencrypt",
  "issuer": "Let's Encrypt"
}

202, not 201. The order is queued and advanced in the background; poll GET on this path for the chain.

The names come from the hostname you registered, never from the csr. A request naming anything else is a 400 - that check is what stops a certificate being issued for a name you do not hold.

One order at a time per hostname. Two in flight publish four challenge values where only the two newest survive, so the older one would fail validation.

Rate limited per account. Each accepted request spends a certificate against the CA's per-domain cap, so the limit is lower than the other write endpoints.

Nothing renews this for you. Watch expires_at on the GET and POST a fresh request before it passes.

Leave the CSR subject empty. A CN is allowed but must be the hostname; the CAs read the SAN extension and reject a CN that is not in it.

StatusCodeWhen
400BAD_REQUESTThe csr is missing, unreadable, unsigned by its own key, or names anything other than this hostname
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks hostnames:write, or is narrowed to another hostname
404NOT_FOUNDHostname not found
409CONFLICTThe hostname is certificate: "self" or "managed", or an order for it is already in progress
429RATE_LIMITEDMore certificate requests than certificateOrderPerMinute allows for this team

DNS-01 certificate API

An acme-dns-compatible API publishes _acme-challenge TXT records for a hostname, which enables wildcard certificates and certificates for machines on private networks, on every plan. A credential publishes challenges for exactly one hostname; creating and updating hostnames, and minting these credentials, all use your account API key.

POST /api/v1/hostnames/:id/acme-credential

Mints a credential for the hostname (API-key auth, same as other v1 endpoints). The password appears in this response exactly once. At most 5 credentials per hostname, and at most 10 of these calls a minute per account.

Parameters

  • id (path) The hostname the credential may publish challenges for.
$ curl -X POST https://dnsmint.com/api/v1/hostnames/HOST_ID/acme-credential \
    -H "Authorization: Bearer $DNSMINT_KEY"
{
  "username": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
  "password": "f3a9...",
  "fulldomain": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev",
  "subdomain": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
  "server_url": "https://dnsmint.com/api/acme",
  "allowfrom": []
}
StatusCodeWhen
400BAD_REQUESTCredential limit reached (5)
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks the dns01:write scope, or is narrowed to another hostname
404NOT_FOUNDHostname not found
409CONFLICTHostname is released, suspended or terminated; a pending hostname is served and is not refused
429RATE_LIMITEDMore than 10 credential mints in a minute in this team
500INTERNAL_ERRORUnexpected server error

POST /api/v1/hostnames/:id/acme-challenge

Publish the DNS-01 challenge value directly, with the API key. The acme-dns route above needs a minted credential, whose password is shown once and capped at five per hostname; a client that keeps no state would exhaust that in five renewals. This route takes the same dns01:write authorization, narrowed to the hostname if the key is, and writes the same record under the same two-newest rule.

Parameters

  • txt (body, required) The DNS-01 challenge value your ACME client computed. Exactly 43 base64url characters.
$ 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"}

The hostname must be live. The two newest values for the name are served, which covers the apex plus wildcard double validation.

This route, DELETE on it and /acme/update share one budget per hostname, so a caller holding both paths cannot double it.

StatusCodeWhen
400BAD_REQUESTInvalid JSON, or txt is not a 43-character challenge value
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks dns01:write, or is narrowed to another hostname
404NOT_FOUNDHostname not found
409CONFLICTHostname is released, suspended or terminated; a pending hostname is served and is not refused
429RATE_LIMITEDMore than 20 challenge updates in a minute on this hostname, across this route, DELETE and /acme/update
500INTERNAL_ERRORUnexpected server error

DELETE /api/v1/hostnames/:id/acme-challenge

Withdraw a challenge value after validation. Removing a value that is already gone returns 200 with removed: false rather than an error: a client's cleanup runs whether or not its publish completed, so it has to be callable unconditionally.

Parameters

  • txt (body, required) The value to withdraw, exactly as published.
$ 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}
StatusCodeWhen
400BAD_REQUESTInvalid JSON, or txt is not a 43-character challenge value
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks dns01:write, or is narrowed to another hostname
404NOT_FOUNDHostname not found
429RATE_LIMITEDMore than 20 challenge updates in a minute on this hostname
500INTERNAL_ERRORUnexpected server error

POST /api/httpreq/present

The endpoint lego's built-in httpreq provider posts to, so lego and Traefik issue for a DNSMint hostname with three environment variables and nothing to pre-seed. lego sends {fqdn, value} with HTTP basic auth; the password is your API key, and the username can be anything but has to be set, because lego sends basic auth only when both are. Authorization is the key's: dns01:write, narrowed to the hostname if the key is. The hostname is read from the fqdn, which for a wildcard order is the bare name, so one hostname covers *.hostname too.

Parameters

  • fqdn (body, required) The challenge name lego computed: _acme-challenge. followed by the hostname, trailing dot optional.
  • value (body, required) The DNS-01 challenge value. Exactly 43 base64url characters.
$ curl -X POST https://dnsmint.com/api/httpreq/present \
    -u ":$DNSMINT_KEY" \
    -H "Content-Type: application/json" \
    -d '{"fqdn": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev.", "value": "CHALLENGE_VALUE_43_CHARS"}'
{"fqdn": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev.", "value": "CHALLENGE_VALUE_43_CHARS"}

RAW mode (HTTPREQ_MODE=RAW, body {domain, token, keyAuth}) is refused with a 400 naming the fix. The default mode carries the finished value, which is all a DNS server needs.

Shares the per-hostname budget with /acme/update and the direct route above.

lego and Traefik: point the provider at this endpoint, set any username, and give it the key as the password. Nothing else - no credential, no storage file. lego sends basic auth only when both username and password are set, so the username is required even though we ignore its value.

HTTPREQ_ENDPOINT=https://dnsmint.com/api/httpreq
HTTPREQ_USERNAME=dnsmint
HTTPREQ_PASSWORD=<your DNSMint API key>

# lego
lego --email you@example.com --dns httpreq \
    -d q7k4m2.dnsmint-a3f9c1.dev -d '*.q7k4m2.dnsmint-a3f9c1.dev' run

# Traefik (static configuration), same two variables in the environment
certificatesResolvers:
  dnsmint:
    acme:
      email: you@example.com
      storage: /letsencrypt/acme.json
      dnsChallenge:
        provider: httpreq
StatusCodeWhen
400BAD_REQUESTInvalid JSON, fqdn is not _acme-challenge.HOSTNAME, value is not 43 characters, or HTTPREQ_MODE=RAW
401UNAUTHORIZEDMissing or invalid API key in the basic-auth password
403FORBIDDENKey lacks dns01:write, or is narrowed to another hostname
404NOT_FOUNDNo hostname by that name on this account
409CONFLICTHostname is released, suspended or terminated; a pending hostname is served and is not refused
429RATE_LIMITEDMore than 20 challenge updates in a minute on this hostname
500INTERNAL_ERRORUnexpected server error

POST /api/httpreq/cleanup

The cleanup half of lego's httpreq provider: same body and authentication as /present. Withdrawing a value that is already gone returns 200, because lego calls cleanup whether or not present completed.

Parameters

  • fqdn (body, required) The challenge name, as sent to /present.
  • value (body, required) The value to withdraw, exactly as published.
$ curl -X POST https://dnsmint.com/api/httpreq/cleanup \
    -u ":$DNSMINT_KEY" \
    -H "Content-Type: application/json" \
    -d '{"fqdn": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev.", "value": "CHALLENGE_VALUE_43_CHARS"}'
{"fqdn": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev.", "value": "CHALLENGE_VALUE_43_CHARS"}
StatusCodeWhen
400BAD_REQUESTInvalid JSON, fqdn is not _acme-challenge.HOSTNAME, value is not 43 characters, or HTTPREQ_MODE=RAW
401UNAUTHORIZEDMissing or invalid API key in the basic-auth password
403FORBIDDENKey lacks dns01:write, or is narrowed to another hostname
404NOT_FOUNDNo hostname by that name on this account
429RATE_LIMITEDMore than 20 challenge updates in a minute on this hostname
500INTERNAL_ERRORUnexpected server error

POST /api/acme/update

The acme-dns wire protocol. Authentication is the credential, sent as X-Api-User and X-Api-Key headers. subdomain must be the credential’s username; txt is the 43-character challenge value. The two newest values per hostname are served, which covers Let’s Encrypt’s apex plus wildcard double validation. A credential only works while its hostname is live, so a released, suspended, or terminated hostname stops accepting challenges. Returns 429 past 20 updates a minute per credential.

Parameters

  • subdomain (body, required) The subdomain from the registration blob, which is also the X-Api-User. It must match the credential that authenticated: a credential publishes for one hostname and nothing else.
  • txt (body, required) The DNS-01 challenge value your ACME client computed. Exactly 43 base64url characters, which is what a SHA-256 key authorization digest is.
$ curl -X POST https://dnsmint.com/api/acme/update \
    -H "X-Api-User: 2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b" \
    -H "X-Api-Key: f3a9..." \
    -d '{"subdomain": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b", "txt": "CHALLENGE_VALUE_43_CHARS"}'
{"txt": "CHALLENGE_VALUE_43_CHARS"}

This is the only acme-dns endpoint we implement. There is no /register: it is unauthenticated and carries no hostname, so there would be nothing to scope a credential to. Credentials come from the endpoint above instead. Clients that auto-register when their storage has no entry for a domain - lego, and Traefik, which embeds it - need that storage pre-seeded with the JSON from acme-credential, keyed by hostname. certbot and cert-manager read a pre-provisioned file already and need nothing extra.

Pre-seeding lego or Traefik: point both at our base URL and at a storage file, and write the response from POST /v1/hostnames/{id}/acme-credential into that file under the hostname before the first run. lego then finds the entry and skips /register. The key is the hostname without any wildcard prefix; one entry covers the hostname and *.hostname.

ACME_DNS_API_BASE=https://dnsmint.com/api/acme
ACME_DNS_STORAGE_PATH=/etc/lego/acme-dns.json

# /etc/lego/acme-dns.json
{
  "q7k4m2.dnsmint-a3f9c1.dev": {
    "username": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
    "password": "f3a9...",
    "fulldomain": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev",
    "subdomain": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
    "server_url": "https://dnsmint.com/api/acme"
  }
}
StatusCodeWhen
400BAD_REQUESTMalformed challenge value
401UNAUTHORIZEDInvalid acme-dns credentials
403FORBIDDENSubdomain does not belong to this credential
429RATE_LIMITEDMore than 20 challenge updates in a minute on this credential
500INTERNAL_ERRORUnexpected server error

Managing keys

A key carrying keys:write can create and revoke keys, so credentials rotate without a person in the loop. Rotation is two calls: mint the replacement, deploy it, then revoke the outgoing key.

Three rules keep that from widening what a key can do. A key can only grant within its own reach - one narrowed to a domain cannot create a team-wide key, or reach a domain it does not hold. The scope is team-wide or nothing, because a key is an account-level object and narrowing it to a domain would be decoration. And it cannot share a key with dns01:write, which is the scope meant for an exposed web server: a machine that renews certificates should not also be able to issue itself new credentials.

Those last two together would make a DNS-01 key impossible to mint 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 has to name a hostname: per-machine keys are what this is for, and no domain-wide or team-wide DNS-01 key can be created through the API at all. The key doing the handing out still cannot publish a challenge itself, and the key it mints still cannot mint another.

A key created without naming scopes never gets keys:write. It is asked for explicitly or not held at all.

GET /api/v1/keys

Lists every live key on the team, metadata only. Secrets are never returned here - a secret exists once, in the response that created it, and is not recoverable afterwards.

$ curl https://dnsmint.com/api/v1/keys \
    -H "Authorization: Bearer $DNSMINT_KEY"

Each entry says what the key reaches, when it was last used, when it expires, and which key created it.

{
  "keys": [
    {
      "key_id": "4e378b700a9e",
      "name": "ci",
      "scopes": [
        {
          "scope": "dns01:write",
          "on": "q7k4m2.example.dev"
        }
      ],
      "created_at": "2026-09-05T10:00:00.000Z",
      "last_used_at": "2026-09-05T11:02:00.000Z",
      "expires_at": "2026-12-04T10:00:00.000Z",
      "created_by_key_id": null,
      "expired": false
    }
  ]
}

created_by_key_id is null when a person created the key in the dashboard, and names the minting key when a program did. That is what makes a leaked keys:write key containable: you can enumerate what it created rather than revoking everything.

StatusCodeWhen
401UNAUTHORIZEDNo Bearer header, or a key that does not match a live credential.
401KEY_EXPIREDThe key was real and its lifetime has passed. Retrying will not help; mint a new key.
403FORBIDDENThe key does not carry `keys:write`. It is team-wide and must be named explicitly when the key is created.

POST /api/v1/keys

Mints a key and returns the secret once. This is the only response that ever contains it. Requires the keys:write scope, which is team-wide and is never granted to a key created without naming scopes.

Parameters

  • name (body, optional) A label, up to 60 characters. Defaults to "api".
  • scopes (body, optional) Scope strings for the whole team, or objects narrowing one to a domain or hostname. Omitted, the key gets hostnames:read, hostnames:write and dns01:write on the whole team - never keys:write, which must be named.
  • expires_in_days (body, optional) 1 to 3650. Omitted or null 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}'

201, and the only copy of the secret.

{
  "key": "dnsm_9b21c5f04a7e_1f3d\u2026",
  "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",
  "expired": false
}

A key can only grant within its own reach. A key narrowed to one domain cannot create a team-wide key, or reach a domain it does not hold; either is refused with 400. Without that, every narrowing on the team would be advisory. A key carrying hostnames:write may grant dns01:write on a hostname it reaches without holding it; that grant must name a hostname, never a domain or the team.

keys:write cannot be combined with dns01:write on one key. DNS-01 belongs on an exposed web server, and a key that can also mint credentials would turn a compromise there into a permanent one. Use two keys.

A key may mint a successor that also carries keys:write. That is rotation, not escalation - mint the replacement, then revoke the outgoing key.

StatusCodeWhen
400BAD_REQUESTUnknown scope, a `keys:write` grant narrowed to a domain, `keys:write` paired with `dns01:write`, a grant exceeding the creating key's own reach, an `expires_in_days` outside 1-3650, a name the team does not own, or the key cap reached.
401UNAUTHORIZEDNo Bearer header, or a key that does not match a live credential.
401KEY_EXPIREDThe key was real and its lifetime has passed. Retrying will not help; mint a new key.
403FORBIDDENThe key does not carry `keys:write`. It is team-wide and must be named explicitly when the key is created.

DELETE /api/v1/keys/{key_id}

Revokes a key. It stops authenticating on its next request, with no propagation delay and no cached window to wait out.

Parameters

  • key_id (path, required) The key id: the middle segment of the key string, between the dnsm_ prefix and the secret. Returned by every listing.
$ curl -X DELETE https://dnsmint.com/api/v1/keys/9b21c5f04a7e \
    -H "Authorization: Bearer $DNSMINT_KEY"

200 once it is revoked.

{
  "key_id": "9b21c5f04a7e",
  "revoked": true
}

A key may revoke itself. That is what a program does when it detects its own compromise, and refusing it would mean the only response available to an automated caller is to wake a person.

Revoking a key does not revoke keys it created. Rotation is mint-then-revoke, so a cascade would destroy the replacement at the moment rotation completed.

A key id that does not exist, is already revoked, or belongs to another team all return the same 404, so key ids on other accounts cannot be probed.

StatusCodeWhen
401UNAUTHORIZEDNo Bearer header, or a key that does not match a live credential.
401KEY_EXPIREDThe key was real and its lifetime has passed. Retrying will not help; mint a new key.
403FORBIDDENThe key does not carry `keys:write`. It is team-wide and must be named explicitly when the key is created.
404NOT_FOUNDNo live key with that id in this team. Already revoked, never existed, and belonging to another team all answer the same way.

Which CA you use

Any of them. Our domains publish a CAA record naming Let's Encrypt and Google Trust Services, so those work through whichever ACME client you already run and nothing else can issue for your hostname by accident.

For anything else - ZeroSSL, Buypass, an internal CA - publish a CAA record on your own hostname with POST /v1/hostnames/{id}/records. It replaces ours for that name, because a CA reads the closest record set to the name it is certifying and ignores everything above it. We serve what you publish rather than merging it with ours, so a record naming only your CA excludes ours for that hostname. Delete it and the domain policy applies again.

We do not restrict the challenge type, so HTTP-01 and TLS-ALPN-01 against your own IP keep working alongside DNS-01.

MCP

Everything above is REST. There is also an MCP server at https://dnsmint.com/mcp, which lets an agent mint a hostname, repoint it and diagnose it without a person having written the config first. It takes an API key as a bearer token, or an OAuth authorization for the connectors that can only be given a URL.

The tools are not listed here on purpose. A client asks the server with tools/list and gets names, argument schemas and descriptions that are always current, and the OAuth endpoints come from the two discovery documents. Copying either into prose would be a second answer that goes stale quietly. The recipe for connecting is on /integrations.

Machine-readable formats

This reference is also published as an OpenAPI 3.1 document at /openapi.json and as a single plain-text file for LLMs at /llms-full.txt.