# Dns01 API

12 operations under /api/v1. Errors are RFC 9457 problem+json with a stable `code` field. The complete schemas are in https://nebulactrl.com/api/v1/openapi.json.

## GET /dns/credentials

List DNS provider credentials

Needs `proxy:write`. Secret variables are never returned; non-secret ones (an access key ID, a region) only to administrators.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `serverId` (query, string (uuid)): Only the team-wide credentials and this server's

Response 200: OK (CredentialsOutputBody)
  - `items` (DNSCredential[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 422 Unprocessable Entity; 500 Internal Server Error

## POST /dns/credentials

Add a DNS provider credential

Needs `admin`. The values are sealed with the instance secret key and delivered to the proxy container as environment variables at deploy time; they are never part of a stored payload and never returned. 422 `invalid_credential` / `invalid_provider`, 409 `credential_name_taken`.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Request body (application/json, required): CreateCredentialInputBody
  - `name` (string, required): Unique per team
  - `provider` (string, required): The lego provider code: one of GET /dns/providers or GET /dns/lego-providers
  - `serverId` (string): Scope the credential to one server; omit for the whole team
  - `values` (map of string, required): Provider variables by name (see GET /dns/providers and GET /dns/lego-providers). Write-only: sealed at rest, never returned.

Response 201: Created (DNSCredential)
  - `createdAt` (string (date-time), required)
  - `fields` (DNSCredentialField[] | null, required)
  - `id` (string, required)
  - `name` (string, required)
  - `provider` (string, required): The lego provider code
  - `providerName` (string, required)
  - `rev` (integer (int64), required): Advances whenever the values change
  - `scope` (enum: team | server, required): team: usable on every server of the team; server: only on ServerID
  - `serverId` (string, required)
  - `updatedAt` (string (date-time), required)
  - `usedBy` (DNSCredentialUse[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 409 Conflict (`credential_name_taken`); 422 Unprocessable Entity (`invalid_credential`); 500 Internal Server Error

## PATCH /dns/credentials/{credentialId}

Rename a credential or rotate its values

Needs `admin`. Changed values stage a proxy deploy on every server whose resolver uses the credential (the proxy container is recreated with the new environment).

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `credentialId` (path, string, required)

Request body (application/json, required): UpdateCredentialInputBody
  - `clear` (string[] | null): Optional variables to remove
  - `name` (string)
  - `values` (map of string): Replaces the named variables; an empty value keeps the stored one

Response 200: OK (DNSCredential)
  - `createdAt` (string (date-time), required)
  - `fields` (DNSCredentialField[] | null, required)
  - `id` (string, required)
  - `name` (string, required)
  - `provider` (string, required): The lego provider code
  - `providerName` (string, required)
  - `rev` (integer (int64), required): Advances whenever the values change
  - `scope` (enum: team | server, required): team: usable on every server of the team; server: only on ServerID
  - `serverId` (string, required)
  - `updatedAt` (string (date-time), required)
  - `usedBy` (DNSCredentialUse[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 409 Conflict; 422 Unprocessable Entity; 500 Internal Server Error

## DELETE /dns/credentials/{credentialId}

Delete a credential

Needs `admin`. 409 `credential_in_use` while a resolver (desired or applied) uses it.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `credentialId` (path, string, required)

Response 204: No Content
Errors: 401 Unauthorized; 403 Forbidden; 409 Conflict (`credential_in_use`); 422 Unprocessable Entity; 500 Internal Server Error

## GET /dns/lego-providers

Every other lego DNS provider, for the generic form

The providers of the lego release Traefik vendors that have no curated form: code, name, the lego docs link and the variables lego reads for it. A credential for one of them is a provider code plus variables from this list. `exec` and `manual` are not offered. Variables that read a file are left out.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Response 200: OK (ProvidersOutputBody)
  - `providers` (PublicProviderBody[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 500 Internal Server Error

## GET /dns/providers

Curated DNS-01 providers and their credential variables

Cloudflare, Amazon Route 53, Hetzner, DigitalOcean, Gandi, OVHcloud, Porkbun, Namecheap, Google Cloud DNS, Azure DNS, deSEC, Linode, Vultr, Scaleway, Bunny, DNSimple, IONOS, GoDaddy and Netcup. The variable names are the ones Traefik's ACME client (lego v5) reads. Any other lego provider is in GET /dns/lego-providers.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Response 200: OK (ProvidersOutputBody1)
  - `items` (DNSProviderSpec[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 500 Internal Server Error

## PATCH /dns01/wildcards/{wildcardId}

Stage a change of a wildcard certificate's options

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `wildcardId` (path, string, required)

Request body (application/json, required): UpdateWildcardInputBody
  - `coverGenerated` (boolean)
  - `default` (boolean)
  - `includeApex` (boolean)

Response 200: OK (ServerDNS01)
  - `acmeEmail` (string, required)
  - `baseDomain` (string, required): The domain generated hostnames live under (the server's base domain)
  - `resolvers` (DNSResolverView[] | null, required)
  - `staged` (boolean, required): Staged DNS-01 changes await Deploy proxy
  - `wildcards` (WildcardCertView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 422 Unprocessable Entity; 500 Internal Server Error

## DELETE /dns01/wildcards/{wildcardId}

Stage the removal of a wildcard certificate

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `wildcardId` (path, string, required)

Response 200: OK (ServerDNS01)
  - `acmeEmail` (string, required)
  - `baseDomain` (string, required): The domain generated hostnames live under (the server's base domain)
  - `resolvers` (DNSResolverView[] | null, required)
  - `staged` (boolean, required): Staged DNS-01 changes await Deploy proxy
  - `wildcards` (WildcardCertView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 422 Unprocessable Entity; 500 Internal Server Error

## GET /servers/{serverId}/dns01

A server's DNS-01 resolvers and wildcard certificates

Desired state with staged additions, updates and removals marked; certificate status and expiry are observed by the agent.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `serverId` (path, string, required)

Response 200: OK (ServerDNS01)
  - `acmeEmail` (string, required)
  - `baseDomain` (string, required): The domain generated hostnames live under (the server's base domain)
  - `resolvers` (DNSResolverView[] | null, required)
  - `staged` (boolean, required): Staged DNS-01 changes await Deploy proxy
  - `wildcards` (WildcardCertView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 422 Unprocessable Entity; 500 Internal Server Error

## PUT /servers/{serverId}/dns01/resolvers/{provider}

Stage the DNS-01 resolver of a provider on a server

Needs `proxy:write`. One resolver per provider and server. Staged until `POST /servers/{id}/proxy/deploy`, which recreates the proxy container. 422 `acme_email_required`, `invalid_credential`, `agent_too_old`.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `serverId` (path, string, required)
- `provider` (path, string, required)

Request body (application/json, required): SetResolverInputBody
  - `credentialId` (string, required)
  - `delaySeconds` (integer (int64)): Wait before checking that the TXT record propagated (slow DNS providers)
  - `staging` (boolean): Use the Let's Encrypt staging CA (untrusted certificates, generous rate limits)

Response 200: OK (ServerDNS01)
  - `acmeEmail` (string, required)
  - `baseDomain` (string, required): The domain generated hostnames live under (the server's base domain)
  - `resolvers` (DNSResolverView[] | null, required)
  - `staged` (boolean, required): Staged DNS-01 changes await Deploy proxy
  - `wildcards` (WildcardCertView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 422 Unprocessable Entity (`acme_email_required`); 500 Internal Server Error

## DELETE /servers/{serverId}/dns01/resolvers/{provider}

Stage the removal of a resolver

409 `resolver_in_use` while a wildcard certificate or a domain of a service on the server uses it.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `serverId` (path, string, required)
- `provider` (path, string, required)

Response 200: OK (ServerDNS01)
  - `acmeEmail` (string, required)
  - `baseDomain` (string, required): The domain generated hostnames live under (the server's base domain)
  - `resolvers` (DNSResolverView[] | null, required)
  - `staged` (boolean, required): Staged DNS-01 changes await Deploy proxy
  - `wildcards` (WildcardCertView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 409 Conflict (`resolver_in_use`); 422 Unprocessable Entity; 500 Internal Server Error

## POST /servers/{serverId}/dns01/wildcards

Stage a wildcard certificate

Needs `proxy:write` and the provider's resolver on the server (422 `resolver_not_configured`). The certificate covers `*.<domain>` (and the apex with `includeApex`); `coverGenerated` lets generated domains use it; `default` makes it the proxy's default certificate. 409 `wildcard_exists`.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `serverId` (path, string, required)

Request body (application/json, required): CreateWildcardInputBody
  - `coverGenerated` (boolean): Generated domains under the base domain use this certificate
  - `default` (boolean): Make it the proxy's default certificate
  - `domain` (string, required): Base domain; the certificate covers *.<domain>
  - `includeApex` (boolean, required): Also cover the base domain itself
  - `provider` (string, required)

Response 201: Created (ServerDNS01)
  - `acmeEmail` (string, required)
  - `baseDomain` (string, required): The domain generated hostnames live under (the server's base domain)
  - `resolvers` (DNSResolverView[] | null, required)
  - `staged` (boolean, required): Staged DNS-01 changes await Deploy proxy
  - `wildcards` (WildcardCertView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 409 Conflict (`wildcard_exists`); 422 Unprocessable Entity (`resolver_not_configured`); 500 Internal Server Error
